Merge remote-tracking branch 'origin/feat/rsync-old-dirs-aliases' into dev
This commit is contained in:
@@ -69,6 +69,9 @@ replacement for every rsync feature or protocol mode.
|
|||||||
- `--partial`, `--partial-dir`, `-P`, `--append`, and `--append-verify` are not
|
- `--partial`, `--partial-dir`, `-P`, `--append`, and `--append-verify` are not
|
||||||
yet full rsync-style resumable transfers. Interrupted files are not retained
|
yet full rsync-style resumable transfers. Interrupted files are not retained
|
||||||
for resumption.
|
for resumption.
|
||||||
|
- `--dirs` is not implemented. Its compatibility aliases `--old-dirs` and
|
||||||
|
`--old-d` are recognized but rejected explicitly rather than silently using
|
||||||
|
FastSync's recursive directory behavior.
|
||||||
- Several rsync short options currently have FastSync-specific meanings. Do
|
- Several rsync short options currently have FastSync-specific meanings. Do
|
||||||
not assume every short option is interchangeable yet.
|
not assume every short option is interchangeable yet.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ This document maps rsync's full feature set to FastSync's current implementation
|
|||||||
| `-r`, `--recursive` | Recurse into directories | ✅ Implemented | Default behavior |
|
| `-r`, `--recursive` | Recurse into directories | ✅ Implemented | Default behavior |
|
||||||
| `-R`, `--relative` | Use relative path names | ❌ Not Implemented | Removed because it had no effect |
|
| `-R`, `--relative` | Use relative path names | ❌ Not Implemented | Removed because it had no effect |
|
||||||
| `--no-implied-dirs` | Don't send implied dirs with -R | ❌ Not Implemented | |
|
| `--no-implied-dirs` | Don't send implied dirs with -R | ❌ Not Implemented | |
|
||||||
| `-d`, `--dirs` | Transfer dirs without recursing | ❌ Not Implemented | |
|
| `-d`, `--dirs`, `--old-dirs`, `--old-d` | Transfer dirs without recursing | ❌ Not Implemented | The aliases are recognized and rejected explicitly; they depend on the unimplemented `--dirs` behavior |
|
||||||
| `--mkpath` | Create missing path components | ❌ Not Implemented | |
|
| `--mkpath` | Create missing path components | ❌ Not Implemented | |
|
||||||
|
|
||||||
## 5. Transfer Modifications
|
## 5. Transfer Modifications
|
||||||
|
|||||||
+18
-6
@@ -335,6 +335,7 @@ typedef enum {
|
|||||||
OPT_POS_INT,
|
OPT_POS_INT,
|
||||||
OPT_NONNEG_INT,
|
OPT_NONNEG_INT,
|
||||||
OPT_ULL,
|
OPT_ULL,
|
||||||
|
OPT_UNSUPPORTED,
|
||||||
} OptKind;
|
} OptKind;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -384,6 +385,8 @@ static const OptionEntry OPTION_TABLE[] = {
|
|||||||
{"--existing", NULL, OPT_FLAG, offsetof(Config, existing)},
|
{"--existing", NULL, OPT_FLAG, offsetof(Config, existing)},
|
||||||
{"--ignore-existing", NULL, OPT_FLAG, offsetof(Config, ignore_existing)},
|
{"--ignore-existing", NULL, OPT_FLAG, offsetof(Config, ignore_existing)},
|
||||||
{"--chmod", NULL, OPT_STRING, offsetof(Config, chmod_spec)},
|
{"--chmod", NULL, OPT_STRING, offsetof(Config, chmod_spec)},
|
||||||
|
{"--dirs", "--old-dirs", OPT_UNSUPPORTED, 0},
|
||||||
|
{"--old-d", NULL, OPT_UNSUPPORTED, 0},
|
||||||
|
|
||||||
{"--source-dir", NULL, OPT_STRING, offsetof(Config, send_directory)},
|
{"--source-dir", NULL, OPT_STRING, offsetof(Config, send_directory)},
|
||||||
{"--dest-dir", NULL, OPT_STRING, offsetof(Config, receive_root_directory)},
|
{"--dest-dir", NULL, OPT_STRING, offsetof(Config, receive_root_directory)},
|
||||||
@@ -491,10 +494,10 @@ static int apply_negation(Config* config, const char* arg) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int apply_table_option(Config* config, const OptionEntry* entry, const char* value) {
|
static int apply_table_option(Config* config, const OptionEntry* entry, const char* option_name,
|
||||||
|
const char* value) {
|
||||||
if (entry->kind == OPT_NOOP)
|
if (entry->kind == OPT_NOOP)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
void* field = (char*)config + entry->offset;
|
void* field = (char*)config + entry->offset;
|
||||||
switch (entry->kind) {
|
switch (entry->kind) {
|
||||||
case OPT_FLAG:
|
case OPT_FLAG:
|
||||||
@@ -517,6 +520,11 @@ static int apply_table_option(Config* config, const OptionEntry* entry, const ch
|
|||||||
*(unsigned long long*)field = v;
|
*(unsigned long long*)field = v;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
case OPT_UNSUPPORTED:
|
||||||
|
log_message(LOG_LEVEL_ERROR,
|
||||||
|
"%s: directory-only transfer is not implemented; refusing to ignore option",
|
||||||
|
option_name);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -592,19 +600,23 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
if (!entry)
|
if (!entry)
|
||||||
entry = find_table_option_with_equals(argv[i], &inline_value);
|
entry = find_table_option_with_equals(argv[i], &inline_value);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
|
const char* option_name = argv[i];
|
||||||
|
const char* value = NULL;
|
||||||
if (entry->kind != OPT_FLAG) {
|
if (entry->kind != OPT_FLAG) {
|
||||||
const char* value = inline_value;
|
if (entry->kind != OPT_UNSUPPORTED) {
|
||||||
|
value = inline_value;
|
||||||
if (!value && i + 1 < argc)
|
if (!value && i + 1 < argc)
|
||||||
value = argv[++i];
|
value = argv[++i];
|
||||||
if (!value) {
|
if (!value) {
|
||||||
log_message(LOG_LEVEL_ERROR, "missing argument for %s", entry->name);
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", entry->name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (strcmp(entry->name, "--compress-choice") == 0) {
|
if (strcmp(entry->name, "--compress-choice") == 0) {
|
||||||
if (set_compression_choice(config, value) != 0)
|
if (set_compression_choice(config, value) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
if (apply_table_option(config, entry, value) != 0)
|
if (apply_table_option(config, entry, option_name, value) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (strcmp(entry->name, "--compress-level") == 0 &&
|
if (strcmp(entry->name, "--compress-level") == 0 &&
|
||||||
(config->compression_level < 1 || config->compression_level > 22)) {
|
(config->compression_level < 1 || config->compression_level > 22)) {
|
||||||
@@ -620,7 +632,7 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
config->use_metadata = true;
|
config->use_metadata = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (apply_table_option(config, entry, NULL) != 0) {
|
} else if (apply_table_option(config, entry, option_name, NULL) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (entry->offset == offsetof(Config, eight_bit_output))
|
if (entry->offset == offsetof(Config, eight_bit_output))
|
||||||
@@ -839,7 +851,7 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set_log_level(config->quiet ? LOG_LEVEL_ERROR : (verbose ? LOG_LEVEL_DEBUG : LOG_LEVEL_WARNING));
|
set_log_level(config->quiet ? LOG_LEVEL_ERROR : (verbose ? LOG_LEVEL_DEBUG : LOG_LEVEL_WARNING));
|
||||||
if (config->compress_choice)
|
if (config->compress_choice)
|
||||||
config->use_compression = strcmp(config->compress_choice, "zstd") == 0;
|
config->use_compression = strcmp(config->compress_choice, "zstd") == 0;
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -25,6 +25,8 @@ void print_usage(void) {
|
|||||||
printf(" -8, --8-bit-output Leave high-bit characters unescaped in output\n");
|
printf(" -8, --8-bit-output Leave high-bit characters unescaped in output\n");
|
||||||
printf(" --delete Delete files on receiver not in source\n");
|
printf(" --delete Delete files on receiver not in source\n");
|
||||||
printf(" --ignore-existing Skip files that already exist on receiver\n");
|
printf(" --ignore-existing Skip files that already exist on receiver\n");
|
||||||
|
printf(
|
||||||
|
" --dirs, --old-dirs, --old-d Transfer directories without recursing (not implemented)\n");
|
||||||
printf(" --exclude <pattern> Exclude files matching pattern\n");
|
printf(" --exclude <pattern> Exclude files matching pattern\n");
|
||||||
printf(" --include <pattern> Only include files matching pattern\n");
|
printf(" --include <pattern> Only include files matching pattern\n");
|
||||||
printf(" --exclude-from <file> Read exclude patterns from file\n");
|
printf(" --exclude-from <file> Read exclude patterns from file\n");
|
||||||
@@ -96,7 +98,7 @@ void print_usage(void) {
|
|||||||
printf(" --inplace Update files in-place (no temp+rename)\n");
|
printf(" --inplace Update files in-place (no temp+rename)\n");
|
||||||
printf(" --fsync Fsync every written file before publication\n");
|
printf(" --fsync Fsync every written file before publication\n");
|
||||||
printf(" --compress-level <n> Compression level (default: 5)\n");
|
printf(" --compress-level <n> Compression level (default: 5)\n");
|
||||||
printf(" --zl <n> Alias for --compress-level\n");
|
printf(" --zl <n> Alias for --compress-level\n");
|
||||||
printf(" --skip-compress=LIST Skip compression for comma-separated suffixes\n");
|
printf(" --skip-compress=LIST Skip compression for comma-separated suffixes\n");
|
||||||
printf(" --compress-threads <n> Compression worker threads (requires zstd threaded support)\n");
|
printf(" --compress-threads <n> Compression worker threads (requires zstd threaded support)\n");
|
||||||
printf(" --no-OPTION Disable a supported boolean option\n");
|
printf(" --no-OPTION Disable a supported boolean option\n");
|
||||||
|
|||||||
@@ -546,6 +546,34 @@ static void test_parse_args_unknown_option() {
|
|||||||
config_delete(cfg);
|
config_delete(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Directory aliases must report the unsupported directory-only behavior clearly. */
|
||||||
|
static void test_parse_args_rejects_dirs_aliases() {
|
||||||
|
static const char* const options[] = {"--dirs", "--old-dirs", "--old-d"};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||||
|
Config* cfg = config_create();
|
||||||
|
char* argv[] = {"fastsync", (char*)options[i], "/src", "/dst"};
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
FILE* log_file = tmpfile();
|
||||||
|
char log_buffer[256] = {0};
|
||||||
|
|
||||||
|
EXPECT_NOT_NULL(log_file);
|
||||||
|
log_set_file(log_file);
|
||||||
|
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1);
|
||||||
|
fflush(log_file);
|
||||||
|
rewind(log_file);
|
||||||
|
EXPECT_TRUE(fread(log_buffer, 1, sizeof(log_buffer) - 1, log_file) > 0);
|
||||||
|
EXPECT_TRUE(strstr(log_buffer, options[i]) != NULL);
|
||||||
|
EXPECT_TRUE(strstr(log_buffer, "directory-only transfer is not implemented") != NULL);
|
||||||
|
EXPECT_TRUE(strstr(log_buffer, "requires --dirs") == NULL);
|
||||||
|
log_set_file(NULL);
|
||||||
|
fclose(log_file);
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Parsed-but-unimplemented options must fail instead of being silently accepted. */
|
/* Parsed-but-unimplemented options must fail instead of being silently accepted. */
|
||||||
static void test_parse_args_rejects_unimplemented_options() {
|
static void test_parse_args_rejects_unimplemented_options() {
|
||||||
static const char* const options[] = {"--silent",
|
static const char* const options[] = {"--silent",
|
||||||
@@ -1081,6 +1109,7 @@ void test_client_cli() {
|
|||||||
test_parse_args_max_alloc_sizes();
|
test_parse_args_max_alloc_sizes();
|
||||||
test_parse_args_rejects_invalid_max_alloc();
|
test_parse_args_rejects_invalid_max_alloc();
|
||||||
test_parse_args_unknown_option();
|
test_parse_args_unknown_option();
|
||||||
|
test_parse_args_rejects_dirs_aliases();
|
||||||
test_parse_args_rejects_unimplemented_options();
|
test_parse_args_rejects_unimplemented_options();
|
||||||
test_parse_args_quiet();
|
test_parse_args_quiet();
|
||||||
test_parse_args_human_readable();
|
test_parse_args_human_readable();
|
||||||
|
|||||||
Reference in New Issue
Block a user