diff --git a/src/client/change_list.c b/src/client/change_list.c index 9f3d728..a2c5096 100644 --- a/src/client/change_list.c +++ b/src/client/change_list.c @@ -245,7 +245,7 @@ static char* change_render_name_uptodate(const ChangeEvent* event) { * resolves to xxh128, so an explicit selection and the default both render the * selected algorithm's digest. */ static ChecksumAlgo out_format_checksum_algo(const Config* config) { - return (ChecksumAlgo)config->checksum_transfer_algo; + return (ChecksumAlgo)config->cli.checksum_transfer_algo; } /* Render a digest as rsync's sum_as_hex: xxh128 prints the HIGH 64-bit half diff --git a/src/client/client_cli.c b/src/client/client_cli.c index fb0411b..12f0c6d 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -170,7 +170,7 @@ static int set_positive_int_option(int* dest, const char* value, const char* opt * name is a hard error with rsync's exit code 4, never a silent no-op. */ static int set_compression_choice(Config* config, const char* value) { if (!value) { - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } int algo; @@ -178,7 +178,7 @@ static int set_compression_choice(Config* config, const char* value) { algo = compression_choice_resolve(); if (algo < 0) { log_message(LOG_LEVEL_ERROR, "RSYNC_COMPRESS_LIST names no supported compression algorithm"); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } } else { @@ -189,7 +189,7 @@ static int set_compression_choice(Config* config, const char* value) { "--compress-choice '%s' is not a supported algorithm; FastSync supports zstd, " "lz4, zlib, zlibx, none or auto", value); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } const char* canonical = compression_algo_name((CompressionAlgo)algo); @@ -226,7 +226,7 @@ static int resolve_checksum_name(const char* name, size_t len, int* out) { * resolves to FastSync's negotiated default (xxh128). */ static int set_checksum_choice(Config* config, const char* value) { if (!value) { - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } const char* comma = strchr(value, ','); @@ -244,7 +244,7 @@ static int set_checksum_choice(Config* config, const char* value) { "--checksum-choice '%s' is invalid; FastSync supports xxh64 (or xxhash), xxh128, " "xxh3, md5, md4, sha1, none or auto, optionally as 'transfer,pre-transfer'", value); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } int negotiated = -1; @@ -252,7 +252,7 @@ static int set_checksum_choice(Config* config, const char* value) { negotiated = checksum_choice_resolve(); if (negotiated < 0) { log_message(LOG_LEVEL_ERROR, "RSYNC_CHECKSUM_LIST names no supported checksum algorithm"); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } } @@ -264,8 +264,8 @@ static int set_checksum_choice(Config* config, const char* value) { pre = negotiated; config->checksum_algo = pre; - config->checksum_transfer_algo = transfer; - config->checksum_choice_set = true; + config->cli.checksum_transfer_algo = transfer; + config->cli.checksum_choice_set = true; /* rsync: "none" for the transfer checksum forces --whole-file. */ if (transfer == (int)CHECKSUM_ALGO_NONE) config->whole_file = true; @@ -963,7 +963,10 @@ static const OptionEntry OPTION_TABLE[] = { * faithful no-op (accepted silently, never consumes an argument). */ {"--recursive", "-r", OPT_NOOP, 0}, {"--update", "-u", OPT_FLAG, offsetof(Config, update)}, - {"--old-args", NULL, OPT_FLAG, offsetof(Config, old_args)}, + /* rsync's --old-args: accepted for CLI compatibility as a documented no-op + * (the remote server path is always safely quoted; see usage.c). It is + * recognized but stores no Config field. */ + {"--old-args", NULL, OPT_NOOP, 0}, {"--rsh", "-e", OPT_STRING, offsetof(Config, rsh_command)}, {"--blocking-io", NULL, OPT_FLAG, offsetof(Config, blocking_io)}, {"--links", "-l", OPT_FLAG, offsetof(Config, follow_symlinks)}, @@ -1196,12 +1199,12 @@ static int apply_negation(Config* config, const char* arg) { config->preserve_times = false; config->preserve_owner = false; config->preserve_group = false; - config->metadata_explicitly_disabled = true; + config->cli.metadata_explicitly_disabled = true; /* --no-preserve is an explicit opt-out of the whole bundle: record it so * the --incremental/--delta auto-preserve in cli_finalize_config does not * silently re-enable perms/times. */ - config->preserve_perms_explicit_off = true; - config->preserve_times_explicit_off = true; + config->cli.preserve_perms_explicit_off = true; + config->cli.preserve_times_explicit_off = true; return 0; } *(bool*)((char*)config + entry->offset) = false; @@ -1209,9 +1212,9 @@ static int apply_negation(Config* config, const char* arg) { * auto-preserve the OTHER attribute without undoing this one. A later * -p/-t sets the attribute directly; this flag only gates the implication. */ if (entry->offset == offsetof(Config, preserve_perms)) - config->preserve_perms_explicit_off = true; + config->cli.preserve_perms_explicit_off = true; else if (entry->offset == offsetof(Config, preserve_times)) - config->preserve_times_explicit_off = true; + config->cli.preserve_times_explicit_off = true; return 0; } @@ -1440,7 +1443,7 @@ static bool cli_handle_range_time_options(CliParseCtx* ctx) { ctx->exit_code = -1; return true; } - config->stop_at_set = true; + config->cli.stop_at_set = true; return true; } if (strcmp(arg, "--stop-at") == 0) { @@ -1455,7 +1458,7 @@ static bool cli_handle_range_time_options(CliParseCtx* ctx) { ctx->exit_code = -1; return true; } - config->stop_at_set = true; + config->cli.stop_at_set = true; return true; } const char* threads_prefix = "--compress-threads="; @@ -1526,7 +1529,7 @@ static bool cli_handle_table_option(CliParseCtx* ctx) { return true; } if (entry->offset == offsetof(Config, compression_level)) - config->compression_level_set = true; + config->cli.compression_level_set = true; if (entry->offset == offsetof(Config, chmod_spec)) { mode_t ignored; if (!chmod_apply(0, config->chmod_spec, &ignored)) { @@ -1539,7 +1542,7 @@ static bool cli_handle_table_option(CliParseCtx* ctx) { defaults to 127.0.0.1, so a value check cannot distinguish it). Used by --dry-run to route an explicit remote target to the server. */ if (entry->offset == offsetof(Config, server_host)) - config->server_host_set = true; + config->cli.server_host_set = true; } } else if (apply_table_option(config, entry, NULL) != 0) { ctx->exit_code = -1; @@ -1813,7 +1816,7 @@ static bool cli_handle_transfer_flags(CliParseCtx* ctx) { return true; } config->compression_level = (int)level; - config->compression_level_set = true; + config->cli.compression_level_set = true; log_info_message(LOG_INFO_MISC, "Set Compression level to %ld", level); ctx->i++; } @@ -1877,7 +1880,7 @@ static int set_server_port_option(Config* config, const char* value, const char* return -1; } config->server_port = port; - config->server_port_set = true; + config->cli.server_port_set = true; return 0; } @@ -2648,7 +2651,7 @@ static int cli_finalize_config(Config* config, bool verbose, bool no_delta, bool int resolved = compression_choice_resolve(); if (resolved < 0) { log_message(LOG_LEVEL_ERROR, "RSYNC_COMPRESS_LIST names no supported compression algorithm"); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } config->compression_algo = resolved; @@ -2659,7 +2662,7 @@ static int cli_finalize_config(Config* config, bool verbose, bool no_delta, bool * clamped to the codec's range, otherwise the codec's own default is used. */ if (config->use_compression) { CompressionAlgo algo = (CompressionAlgo)config->compression_algo; - config->compression_level = config->compression_level_set + config->compression_level = config->cli.compression_level_set ? compression_clamp_level(algo, config->compression_level) : compression_default_level(algo); log_debug_message(LOG_DEBUG_UTIL, "Client compression: %s (level %d)", @@ -2668,22 +2671,22 @@ static int cli_finalize_config(Config* config, bool verbose, bool no_delta, bool /* The negotiated checksum is always resolved (rsync negotiates one for the * delta strong sum even without --checksum): RSYNC_CHECKSUM_LIST first, then * the compiled-in order. An explicit --checksum-choice already set it. */ - if (!config->checksum_choice_set) { + if (!config->cli.checksum_choice_set) { int resolved = checksum_choice_resolve(); if (resolved < 0) { log_message(LOG_LEVEL_ERROR, "RSYNC_CHECKSUM_LIST names no supported checksum algorithm"); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } config->checksum_algo = resolved; - config->checksum_transfer_algo = resolved; + config->cli.checksum_transfer_algo = resolved; } /* rsync parity: "none" as the pre-transfer checksum cannot be combined with * --checksum (exit 4). The check runs here because --checksum may appear on * either side of --checksum-choice. */ if (config->checksum && config->checksum_algo == (int)CHECKSUM_ALGO_NONE) { log_message(LOG_LEVEL_ERROR, "Invalid checksum-choice for --checksum: none"); - config->cli_exit_code = 4; + config->cli.cli_exit_code = 4; return -1; } @@ -2762,11 +2765,11 @@ static int cli_finalize_config(Config* config, bool verbose, bool no_delta, bool * explicitly negated them (--no-perms/--no-times/--no-preserve). This runs * BEFORE the derived use_metadata bit so the transport frame is still sent * for the incremental/delta handshake even when both attributes were negated - * via --no-preserve (metadata_explicitly_disabled handles that opt-out). */ - if (preserve_implied && !config->metadata_explicitly_disabled) { - if (!config->preserve_perms_explicit_off) + * via --no-preserve (cli.metadata_explicitly_disabled handles that opt-out). */ + if (preserve_implied && !config->cli.metadata_explicitly_disabled) { + if (!config->cli.preserve_perms_explicit_off) config->preserve_perms = true; - if (!config->preserve_times_explicit_off) + if (!config->cli.preserve_times_explicit_off) config->preserve_times = true; } @@ -3153,7 +3156,7 @@ int main(int argc, char* argv[]) { int parse_ret = parse_args(config, argc, argv, positional_args, &positional_count); if (parse_ret != 0) { if (parse_ret < 0) - exit_code = config->cli_exit_code ? config->cli_exit_code : 1; + exit_code = config->cli.cli_exit_code ? config->cli.cli_exit_code : 1; goto cleanup; } diff --git a/src/client/client_manifest.c b/src/client/client_manifest.c index d476bb3..55a3f67 100644 --- a/src/client/client_manifest.c +++ b/src/client/client_manifest.c @@ -31,7 +31,7 @@ bool dry_run_targets_server(const Config* config) { return true; if (config->module && config->module[0] != '\0') return true; - if (config->server_host_set || config->server_port_set) + if (config->cli.server_host_set || config->cli.server_port_set) return true; if (config->use_tls) return true; diff --git a/src/client/client_send.c b/src/client/client_send.c index 3b77503..2f3422b 100644 --- a/src/client/client_send.c +++ b/src/client/client_send.c @@ -1578,7 +1578,7 @@ static bool send_files_run(Config* config, SendFilesState* state) { now_mono.tv_nsec = 0; } state->stop = stop_condition_make(config->stop_after_mins > 0, config->stop_after_mins, - config->stop_at_set, config->stop_at, now_mono); + config->cli.stop_at_set, config->stop_at, now_mono); state->prepared.options.stop_condition = &state->stop; /* The early-delete pre-scan above already ran; only the data pass should feed the directory-time list (otherwise every directory would be captured @@ -1916,7 +1916,7 @@ int send_files_multithreaded(Config* config) { now_mono.tv_nsec = 0; } context->stop_condition = - stop_condition_make(config->stop_after_mins > 0, config->stop_after_mins, config->stop_at_set, + stop_condition_make(config->stop_after_mins > 0, config->stop_after_mins, config->cli.stop_at_set, config->stop_at, now_mono); bool collect_excluded = config->use_delete && !config->delete_excluded; unsigned long long pre_scan_non_dir = 0; diff --git a/src/shared/config.c b/src/shared/config.c index f4b255d..eea3974 100644 --- a/src/shared/config.c +++ b/src/shared/config.c @@ -20,9 +20,9 @@ static void config_set_defaults(Config* config) { config->scanner_threads = 0; - config->metadata_explicitly_disabled = false; - config->preserve_perms_explicit_off = false; - config->preserve_times_explicit_off = false; + config->cli.preserve_perms_explicit_off = false; + config->cli.preserve_times_explicit_off = false; + config->cli.metadata_explicitly_disabled = false; config->show_progress = false; config->compression_threads = 0; config->ssh_port = 22; @@ -44,8 +44,8 @@ static void config_set_defaults(Config* config) { config->tls_ca = NULL; config->server_host = str_dup("127.0.0.1"); config->server_port = 8080; - config->server_port_set = false; - config->server_host_set = false; + config->cli.server_port_set = false; + config->cli.server_host_set = false; /* rsync defaults: --timeout=0 (I/O timeouts disabled) and --contimeout=60. * A value of 0 disables the client's own deadline on both the socket layer * (tcp_set_timeouts) and the protocol layer @@ -68,10 +68,10 @@ static void config_set_defaults(Config* config) { config->human_readable = false; config->ignore_errors = false; config->ignore_missing_args = false; - config->checksum_transfer_algo = CHECKSUM_ALGO_DEFAULT; - config->cli_exit_code = 0; - config->compression_level_set = false; - config->checksum_choice_set = false; + config->cli.checksum_transfer_algo = CHECKSUM_ALGO_DEFAULT; + config->cli.cli_exit_code = 0; + config->cli.compression_level_set = false; + config->cli.checksum_choice_set = false; config->filters = NULL; config->files_from = NULL; config->files_from_set = NULL; @@ -85,7 +85,6 @@ static void config_set_defaults(Config* config) { config->rsh_command = NULL; config->blocking_io = false; config->outbuf = OUTBUF_BLOCK; - config->old_args = false; config->remote_options = NULL; config->remote_option_count = 0; config->address = NULL; @@ -101,7 +100,7 @@ static void config_set_defaults(Config* config) { config->trust_sender = false; config->stop_after_mins = 0; config->stop_at = 0; - config->stop_at_set = false; + config->cli.stop_at_set = false; config->write_batch = NULL; config->only_write_batch = NULL; config->read_batch = NULL; @@ -342,7 +341,7 @@ bool config_derived_use_metadata(const Config* config) { config->chown_uid_set || config->chown_gid_set || config->usermap_count > 0 || config->groupmap_count > 0 || config->update) return true; - return (config->use_incremental || config->use_delta) && !config->metadata_explicitly_disabled; + return (config->use_incremental || config->use_delta) && !config->cli.metadata_explicitly_disabled; } bool config_has_basis(const Config* config) { diff --git a/src/shared/config.h b/src/shared/config.h index c4d7130..3596620 100644 --- a/src/shared/config.h +++ b/src/shared/config.h @@ -342,22 +342,60 @@ typedef enum SuperMode { SUPER_MODE_AUTO = 0, SUPER_MODE_ON = 1, SUPER_MODE_OFF CONFIG_WIRE_CODEC_FIELDS(X) \ CONFIG_WIRE_PROTECT_FIELDS(X) +/* Client-only, CLI-parse bookkeeping (never serialized). These members exist + * only so the client command-line parser can record HOW an option was + * specified (explicitly set, explicitly negated, or a parser-requested exit + * code); no other module and no wire peer ever needs them. Grouping them in + * one nested member keeps the public Config free of client-CLI-only state. */ +typedef struct { + /* Set when the user explicitly turned an attribute off with --no-perms / + * --no-times (long or short form). --incremental/--delta historically + * auto-enabled mode and mtime preservation; these flags let + * cli_finalize_config restore that behavior while still honoring the + * explicit per-attribute negation. A later -p/-t re-enables the attribute + * directly, so the flag only prevents the incremental/delta implication, + * never a POSITIVE request. */ + bool preserve_perms_explicit_off; + bool preserve_times_explicit_off; + /* Set by --no-preserve, the explicit opt-out of the whole preservation + * bundle, so the --incremental/--delta auto-preserve implication stays off. */ + bool metadata_explicitly_disabled; + /* True when --server-port/--port was explicitly given. --dry-run uses it to + * decide whether a real server handshake was requested, so a plain local + * destination (no explicit port) keeps the existing client-side dry-run + * behavior instead of dialing the default 127.0.0.1:8080. */ + bool server_port_set; + /* True when --server-host was explicitly given, and distinct from the + * "127.0.0.1" default: --dry-run uses it to route an explicit remote target + * to the server so it reports receiver state exactly like a real run, + * instead of silently running the client-side manifest. */ + bool server_host_set; + /* Codec-negotiation CLI state. The effective pre-transfer checksum is + * Config->checksum_algo (serialized); checksum_transfer_algo is the rsync + * "transfer" half of a two-name --checksum-choice form (validated and used + * only to mirror rsync's whole-file forcing, since FastSync's per-block + * strong hash is fixed). cli_exit_code carries a parser-requested process + * exit status (rsync uses 4 for an unsupported checksum/compress algorithm) + * so main() can mirror it. */ + int checksum_transfer_algo; + int cli_exit_code; + /* "The user explicitly chose" bits. They let the per-codec default level / + * checksum list be applied only when the corresponding rsync option was + * omitted (an explicit --compress-level / --checksum-choice always wins). */ + bool compression_level_set; + bool checksum_choice_set; + /* True when --stop-at was given. */ + bool stop_at_set; +} ConfigCliParse; + typedef struct Config { /* -j/--threads=N: number of parallel scanner worker threads for the -m * pipeline. 0 (the default, also set by bare -j/--threads) means "use the * scanner's built-in default" (4). CLIENT-ONLY: it is a local scheduling * concern and is NEVER serialized into the wire config frame. */ int scanner_threads; - bool metadata_explicitly_disabled; - /* CLIENT-ONLY (never serialized; not in CONFIG_WIRE_FIELDS). Set when the - * user explicitly turned an attribute off with --no-perms / --no-times (long - * or short form). --incremental/--delta historically auto-enabled mode and - * mtime preservation; these flags let cli_finalize_config restore that - * behavior while still honoring the explicit per-attribute negation. A - * later -p/-t re-enables the attribute directly, so the flag only prevents - * the incremental/delta implication, never a POSITIVE request. */ - bool preserve_perms_explicit_off; - bool preserve_times_explicit_off; + /* Client-only CLI-parse bookkeeping (never serialized). See ConfigCliParse. */ + ConfigCliParse cli; bool show_progress; int compression_threads; int ssh_port; @@ -378,18 +416,6 @@ typedef struct Config { bool use_tls; char* server_host; int server_port; - /* True when --server-port/--port was explicitly given. CLIENT-ONLY (never - * serialized): --dry-run uses it to decide whether a real server handshake - * was requested, so a plain local destination (no explicit port) keeps the - * existing client-side dry-run behavior instead of dialing the default - * 127.0.0.1:8080. */ - bool server_port_set; - /* True when --server-host was explicitly given. CLIENT-ONLY (never - * serialized), and distinct from the "127.0.0.1" default: --dry-run uses it - * to route an explicit remote target to the server so it reports receiver - * state exactly like a real run, instead of silently running the client-side - * manifest. */ - bool server_host_set; char* tls_cert; char* tls_key; char* tls_ca; @@ -435,22 +461,6 @@ typedef struct Config { * enters the keep-set. Implied by --delete-missing-args. */ bool ignore_missing_args; - /* Codec-negotiation CLI state (all client-only, never serialized). The - * effective pre-transfer checksum is Config->checksum_algo (serialized); - * checksum_transfer_algo is the rsync "transfer" half of a two-name - * --checksum-choice form (validated and used only to mirror rsync's - * whole-file forcing, since FastSync's per-block strong hash is fixed). - * cli_exit_code carries a parser-requested process exit status (rsync uses 4 - * for an unsupported checksum/compress algorithm) so main() can mirror it. */ - int checksum_transfer_algo; - int cli_exit_code; - /* Client-only "the user explicitly chose" bits. They let the per-codec - * default level / checksum list be applied only when the corresponding - * rsync option was omitted (an explicit --compress-level / --checksum-choice - * always wins). Never serialized. */ - bool compression_level_set; - bool checksum_choice_set; - // Issue #129: Advanced file selection. These fields are CLIENT-ONLY: they are // never serialized to the wire (the receiver must not learn them). ArrayList* filters; /* --filter=RULE rule strings, in order */ @@ -486,7 +496,6 @@ typedef struct Config { /* --outbuf mode (OutbufMode): stdout/stderr buffering. Client-only launch * concern: NEVER crosses the wire. */ int outbuf; - bool old_args; /* --remote-option=OPT (Phase 5, long form only): one or more extra command-line * options to append to the REMOTE server invocation over SSH. CLIENT-ONLY: * they are composed into the remote command line by ssh_build_remote_command() @@ -556,7 +565,6 @@ typedef struct Config { * process and are NEVER serialized into the config frame. */ int stop_after_mins; /* --stop-after=MINS minutes; 0 when unset */ time_t stop_at; /* --stop-at=... absolute wall-clock deadline */ - bool stop_at_set; /* true when --stop-at was given */ /* Client-only residual-batch paths. A residual batch is a self-contained * single-file record of the whole source tree (full file images using the diff --git a/tests/test_change_list.c b/tests/test_change_list.c index f9ad8a5..ef0f16d 100644 --- a/tests/test_change_list.c +++ b/tests/test_change_list.c @@ -195,7 +195,7 @@ static void test_format_C_padding_uses_transfer_algo() { {CHECKSUM_ALGO_NONE, 2}, }; for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { - config->checksum_transfer_algo = cases[i].algo; + config->cli.checksum_transfer_algo = cases[i].algo; char expected[64]; size_t n = 0; expected[n++] = '['; diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index d86f8f6..e86aec7 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -730,7 +730,7 @@ static void test_parse_args_port_alias() { EXPECT_EQ_INT(cfg->server_port, 9000); /* The default port is 8080; the explicit bit is what lets --dry-run tell an explicit remote target from the default and route to the server. */ - EXPECT_TRUE(cfg->server_port_set); + EXPECT_TRUE(cfg->cli.server_port_set); config_delete(cfg); cfg = config_create(); @@ -738,7 +738,7 @@ static void test_parse_args_port_alias() { positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv_inline, positional_args, &positional_count), 0); EXPECT_EQ_INT(cfg->server_port, 9001); - EXPECT_TRUE(cfg->server_port_set); + EXPECT_TRUE(cfg->cli.server_port_set); config_delete(cfg); cfg = config_create(); @@ -746,7 +746,7 @@ static void test_parse_args_port_alias() { positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv_long, positional_args, &positional_count), 0); EXPECT_EQ_INT(cfg->server_port, 9002); - EXPECT_TRUE(cfg->server_port_set); + EXPECT_TRUE(cfg->cli.server_port_set); config_delete(cfg); } @@ -757,18 +757,18 @@ static void test_parse_args_server_host_sets_routing_bit() { Config* cfg = config_create(); int positional_args[2]; int positional_count = 0; - EXPECT_FALSE(cfg->server_host_set); + EXPECT_FALSE(cfg->cli.server_host_set); char* argv_space[] = {"fastsync", "--server-host", "example.test", "/src", "/dst"}; EXPECT_EQ_INT(parse_args(cfg, 5, argv_space, positional_args, &positional_count), 0); EXPECT_EQ_STR(cfg->server_host, "example.test"); - EXPECT_TRUE(cfg->server_host_set); + EXPECT_TRUE(cfg->cli.server_host_set); config_delete(cfg); cfg = config_create(); char* argv_inline[] = {"fastsync", "--server-host=example.test", "/src", "/dst"}; positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv_inline, positional_args, &positional_count), 0); - EXPECT_TRUE(cfg->server_host_set); + EXPECT_TRUE(cfg->cli.server_host_set); config_delete(cfg); } @@ -1726,7 +1726,7 @@ static void test_parse_args_no_preserve_blocks_implicit_metadata() { EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0); EXPECT_FALSE(cfg->use_metadata); - EXPECT_TRUE(cfg->metadata_explicitly_disabled); + EXPECT_TRUE(cfg->cli.metadata_explicitly_disabled); config_delete(cfg); } } @@ -1777,7 +1777,7 @@ static void test_parse_args_checksum_choice_rejects_unsupported() { int positional_args[2]; int positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), -1); - EXPECT_EQ_INT(cfg->cli_exit_code, 4); + EXPECT_EQ_INT(cfg->cli.cli_exit_code, 4); config_delete(cfg); } } @@ -1817,7 +1817,7 @@ static void test_parse_args_checksum_choice_new_algos() { positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 5, argv4, positional_args, &positional_count), 0); EXPECT_EQ_INT(cfg->checksum_algo, single[i]); - EXPECT_EQ_INT(cfg->checksum_transfer_algo, single[i]); + EXPECT_EQ_INT(cfg->cli.checksum_transfer_algo, single[i]); config_delete(cfg); } @@ -1827,7 +1827,7 @@ static void test_parse_args_checksum_choice_new_algos() { char* argv5[] = {"fastsync", "--cc=sha1,md4", "/checksum/src", "/dst"}; positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv5, positional_args, &positional_count), 0); - EXPECT_EQ_INT(cfg->checksum_transfer_algo, (int)CHECKSUM_ALGO_SHA1); + EXPECT_EQ_INT(cfg->cli.checksum_transfer_algo, (int)CHECKSUM_ALGO_SHA1); EXPECT_EQ_INT(cfg->checksum_algo, (int)CHECKSUM_ALGO_MD4); config_delete(cfg); @@ -1849,14 +1849,14 @@ static void test_parse_args_checksum_none_with_checksum_rejected() { int positional_args[2]; int positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), -1); - EXPECT_EQ_INT(cfg->cli_exit_code, 4); + EXPECT_EQ_INT(cfg->cli.cli_exit_code, 4); config_delete(cfg); cfg = config_create(); char* argv2[] = {"fastsync", "--checksum", "--cc=md5,none", "/checksum/src", "/dst"}; positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 5, argv2, positional_args, &positional_count), -1); - EXPECT_EQ_INT(cfg->cli_exit_code, 4); + EXPECT_EQ_INT(cfg->cli.cli_exit_code, 4); config_delete(cfg); /* "none" as the TRANSFER checksum with a real pre-transfer checksum is @@ -1958,7 +1958,7 @@ static void test_parse_args_compress_choice_parity() { int positional_args[2]; int positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), -1); - EXPECT_EQ_INT(cfg->cli_exit_code, 4); + EXPECT_EQ_INT(cfg->cli.cli_exit_code, 4); config_delete(cfg); } } @@ -2078,6 +2078,9 @@ static void test_parse_args_temp_dir() { config_delete(cfg); } +/* --old-args is accepted for rsync CLI compatibility as a documented no-op (the + * remote server path is always safely quoted); it stores no Config field, so + * parsing it must simply succeed and leave the positional arguments intact. */ static void test_parse_args_old_args() { Config* cfg = config_create(); char* argv[] = {"fastsync", "--old-args", "/src", "/dst"}; @@ -2085,7 +2088,7 @@ static void test_parse_args_old_args() { int positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0); - EXPECT_TRUE(cfg->old_args); + EXPECT_EQ_INT(positional_count, 2); config_delete(cfg); } @@ -2719,7 +2722,7 @@ static void test_parse_args_compression_env_list() { cfg = config_create(); positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1); - EXPECT_EQ_INT(cfg->cli_exit_code, 4); + EXPECT_EQ_INT(cfg->cli.cli_exit_code, 4); config_delete(cfg); unsetenv("RSYNC_COMPRESS_LIST"); } @@ -2734,7 +2737,7 @@ static void test_parse_args_checksum_env_list() { setenv("RSYNC_CHECKSUM_LIST", "md5", 1); EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0); EXPECT_EQ_INT(cfg->checksum_algo, (int)CHECKSUM_ALGO_MD5); - EXPECT_EQ_INT(cfg->checksum_transfer_algo, (int)CHECKSUM_ALGO_MD5); + EXPECT_EQ_INT(cfg->cli.checksum_transfer_algo, (int)CHECKSUM_ALGO_MD5); config_delete(cfg); /* An explicit --cc wins. */ @@ -2750,7 +2753,7 @@ static void test_parse_args_checksum_env_list() { cfg = config_create(); positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1); - EXPECT_EQ_INT(cfg->cli_exit_code, 4); + EXPECT_EQ_INT(cfg->cli.cli_exit_code, 4); config_delete(cfg); unsetenv("RSYNC_CHECKSUM_LIST"); } @@ -4385,7 +4388,7 @@ static void test_parse_args_preserve_long_form() { } /* --no-perms/--no-times/--no-owner/--no-group (long and short) clear only - * their own attribute bit; they never set metadata_explicitly_disabled. */ + * their own attribute bit; they never set cli.metadata_explicitly_disabled. */ static void test_parse_args_preserve_negations() { struct { const char* arg; @@ -4413,7 +4416,7 @@ static void test_parse_args_preserve_negations() { bool expected = all[j] != cases[i].offset; EXPECT_TRUE(*(bool*)((char*)cfg + all[j]) == expected); } - EXPECT_FALSE(cfg->metadata_explicitly_disabled); + EXPECT_FALSE(cfg->cli.metadata_explicitly_disabled); /* -a's devices/specials keep the metadata frame on. */ EXPECT_TRUE(cfg->use_metadata); config_delete(cfg); @@ -4456,7 +4459,7 @@ static void test_parse_args_no_preserve_disables_bundle() { EXPECT_FALSE(cfg->preserve_times); EXPECT_FALSE(cfg->preserve_owner); EXPECT_FALSE(cfg->preserve_group); - EXPECT_TRUE(cfg->metadata_explicitly_disabled); + EXPECT_TRUE(cfg->cli.metadata_explicitly_disabled); EXPECT_TRUE(cfg->use_incremental); EXPECT_FALSE(cfg->use_metadata); config_delete(cfg); @@ -4509,7 +4512,7 @@ static void test_parse_args_incremental_implies_preserve() { EXPECT_EQ_INT(parse_args(cfg, 5, argv4, positional_args, &positional_count), 0); EXPECT_FALSE(cfg->preserve_perms); EXPECT_FALSE(cfg->preserve_times); - EXPECT_TRUE(cfg->metadata_explicitly_disabled); + EXPECT_TRUE(cfg->cli.metadata_explicitly_disabled); EXPECT_FALSE(cfg->use_metadata); config_delete(cfg); } diff --git a/tests/test_config.c b/tests/test_config.c index a801d70..4c87b34 100644 --- a/tests/test_config.c +++ b/tests/test_config.c @@ -2533,15 +2533,15 @@ static void test_config_derived_use_metadata() { /* Incremental/delta imply metadata unless --no-preserve disabled it. */ c->use_incremental = true; EXPECT_TRUE(config_derived_use_metadata(c)); - c->metadata_explicitly_disabled = true; + c->cli.metadata_explicitly_disabled = true; EXPECT_FALSE(config_derived_use_metadata(c)); - c->metadata_explicitly_disabled = false; + c->cli.metadata_explicitly_disabled = false; c->use_incremental = false; c->use_delta = true; EXPECT_TRUE(config_derived_use_metadata(c)); - c->metadata_explicitly_disabled = true; + c->cli.metadata_explicitly_disabled = true; EXPECT_FALSE(config_derived_use_metadata(c)); - c->metadata_explicitly_disabled = false; + c->cli.metadata_explicitly_disabled = false; c->use_delta = false; /* Flags that must NOT imply metadata on their own. */