feat(p6-iconv): --iconv charset conversion + PROTOCOL 2.16.0
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "client_send.h"
|
||||
#include "client_validation.h"
|
||||
#include "charset.h"
|
||||
#include "chmod.h"
|
||||
#include "compression.h"
|
||||
#include "config.h"
|
||||
@@ -590,6 +591,11 @@ static const OptionEntry OPTION_TABLE[] = {
|
||||
* path; main() reads it (after the destination form is known) and derives
|
||||
* the wire credentials. Never crosses the wire. */
|
||||
{"--password-file", NULL, OPT_STRING, offsetof(Config, password_file)},
|
||||
/* --iconv (protocol 2.16.0): convert file-NAME charsets at the wire
|
||||
* boundary. The CONVERT_SPEC (LOCAL[,REMOTE]) is validated for real iconv
|
||||
* charsets at startup (client_validation.c) and the full spec rides the
|
||||
* config frame so the receiver derives the wire charset symmetrically. */
|
||||
{"--iconv", NULL, OPT_STRING, offsetof(Config, iconv_spec)},
|
||||
{"--delete-before", NULL, OPT_FLAG, offsetof(Config, delete_before)},
|
||||
{"--delete-during", "--del", OPT_FLAG, offsetof(Config, delete_during)},
|
||||
{"--delete-delay", NULL, OPT_FLAG, offsetof(Config, delete_delay)},
|
||||
@@ -1593,6 +1599,17 @@ int main(int argc, char* argv[]) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* --iconv: install the sender-side local->wire conversion before any path is
|
||||
scanned or serialized (the scanner and the chunk/data path read windows are
|
||||
all driven from this process, so one global initialization covers every
|
||||
send site). */
|
||||
if (!charset_wire_init_sender(config->iconv_spec)) {
|
||||
log_message(LOG_LEVEL_ERROR,
|
||||
"--iconv has an invalid CONVERT_SPEC or an unsupported charset name");
|
||||
exit_code = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Apply the requested --outbuf style now that the mode is parsed. */
|
||||
apply_output_buffering(config);
|
||||
|
||||
@@ -1614,6 +1631,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
cleanup:
|
||||
charset_wire_free();
|
||||
if (config) {
|
||||
config_delete(config);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "client_send.h"
|
||||
#include "array_list.h"
|
||||
#include "change_list.h"
|
||||
#include "charset.h"
|
||||
#include "chunk.h"
|
||||
#include "compression.h"
|
||||
#include "config.h"
|
||||
@@ -810,21 +811,21 @@ static int send_delete_manifest(int fd, ArrayList* manifest, ArrayList* protecte
|
||||
if (!send_int(fd, keep_count))
|
||||
return -1;
|
||||
for (int i = 0; i < keep_count; i++) {
|
||||
if (!send_str(fd, (char*)manifest->items[i]))
|
||||
if (!send_wire_str(fd, (char*)manifest->items[i]))
|
||||
return -1;
|
||||
}
|
||||
int protected_count = protected_prefixes ? protected_prefixes->size : 0;
|
||||
if (!send_int(fd, protected_count))
|
||||
return -1;
|
||||
for (int i = 0; i < protected_count; i++) {
|
||||
if (!send_str(fd, (char*)protected_prefixes->items[i]))
|
||||
if (!send_wire_str(fd, (char*)protected_prefixes->items[i]))
|
||||
return -1;
|
||||
}
|
||||
int missing_count = missing_args ? missing_args->size : 0;
|
||||
if (!send_int(fd, missing_count))
|
||||
return -1;
|
||||
for (int i = 0; i < missing_count; i++) {
|
||||
if (!send_str(fd, (char*)missing_args->items[i]))
|
||||
if (!send_wire_str(fd, (char*)missing_args->items[i]))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -899,7 +900,7 @@ static int incremental_check(Client* client, File* file, const Config* config,
|
||||
*resume_offset = 0;
|
||||
if (!send_status(client->file_descriptor, STATUS_CHECK))
|
||||
return -1;
|
||||
if (!send_str(client->file_descriptor, file_wire_path(file)))
|
||||
if (!send_wire_str(client->file_descriptor, file_wire_path(file)))
|
||||
return -1;
|
||||
unsigned long long fsize = file->data->size;
|
||||
long long mtime = file->metadata ? file->metadata->mtime_sec : 0;
|
||||
@@ -1119,7 +1120,7 @@ static bool send_directory_entry(Client* client, File* file) {
|
||||
return false;
|
||||
if (!send_status(client->file_descriptor, STATUS_MKDIR))
|
||||
return false;
|
||||
return send_str(client->file_descriptor, file_wire_path(file));
|
||||
return send_wire_str(client->file_descriptor, file_wire_path(file));
|
||||
}
|
||||
|
||||
/* Transmit one symlink entry: a STATUS_SYMLINK frame carrying the destination
|
||||
@@ -1130,8 +1131,8 @@ static bool send_symlink_entry(const Client* client, File* file, const Config* c
|
||||
if (!file || !file_wire_path(file) || !file->symlink_target)
|
||||
return false;
|
||||
int fd = client->file_descriptor;
|
||||
if (!send_status(fd, STATUS_SYMLINK) || !send_str(fd, file_wire_path(file)) ||
|
||||
!send_str(fd, file->symlink_target))
|
||||
if (!send_status(fd, STATUS_SYMLINK) || !send_wire_str(fd, file_wire_path(file)) ||
|
||||
!send_wire_str(fd, file->symlink_target))
|
||||
return false;
|
||||
return !config->use_metadata || metadata_send(fd, file->metadata);
|
||||
}
|
||||
@@ -1311,9 +1312,9 @@ static int send_chunk_with_removal(Client* client, Chunk* chunk, Config* config,
|
||||
wire path so the receiver links this entry to that installed file. */
|
||||
if (f->link_group != 0 && !f->link_first && f->hardlink_target != NULL) {
|
||||
if (!send_status(client->file_descriptor, STATUS_HARDLINK) ||
|
||||
!send_str(client->file_descriptor, file_wire_path(f)) ||
|
||||
!send_wire_str(client->file_descriptor, file_wire_path(f)) ||
|
||||
!send_int(client->file_descriptor, f->link_group) ||
|
||||
!send_str(client->file_descriptor, f->hardlink_target))
|
||||
!send_wire_str(client->file_descriptor, f->hardlink_target))
|
||||
return -1;
|
||||
change_emit_file_sent(config, f);
|
||||
continue;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "client_validation.h"
|
||||
#include "charset.h"
|
||||
#include "delay_updates.h"
|
||||
#include "log.h"
|
||||
#include "usage.h"
|
||||
@@ -123,5 +124,13 @@ bool validate_config(const Config* config) {
|
||||
"timing; at most one may be given and each implies --delete");
|
||||
return false;
|
||||
}
|
||||
/* --iconv: reject a malformed CONVERT_SPEC or an unsupported charset name at
|
||||
startup (a probe iconv_open is attempted), so a typo'd charset never fails
|
||||
the run mid-transfer with per-file errors. */
|
||||
if (!charset_spec_valid(config->iconv_spec)) {
|
||||
log_message(LOG_LEVEL_ERROR,
|
||||
"--iconv requires LOCAL[,REMOTE] charset names supported by iconv");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ void print_usage(void) {
|
||||
printf(" --progress Show transfer progress\n");
|
||||
printf(" -P Partial mode with progress (retention incomplete)\n");
|
||||
printf(" -8, --8-bit-output Leave high-bit characters unescaped in output\n");
|
||||
printf(" --iconv=LOCAL[,REMOTE] Convert file-NAME charsets at the wire boundary:\n");
|
||||
printf(" LOCAL is the charset of our file names, REMOTE is the\n");
|
||||
printf(" remote side's charset (defaults to LOCAL). Names are\n");
|
||||
printf(" converted before transmission and back on receipt; a\n");
|
||||
printf(" name that cannot be represented in the target charset\n");
|
||||
printf(" fails that transfer cleanly (rsync-compatible)\n");
|
||||
printf(" --delete Delete files on receiver not in source\n");
|
||||
printf(" (default timing: delete only after the whole\n");
|
||||
printf(" transfer has succeeded)\n");
|
||||
|
||||
Reference in New Issue
Block a user