feat: add 8-bit output option
CI / lint (pull_request) Successful in 11s
CI / sanitizers (address) (pull_request) Successful in 37s
CI / sanitizers (undefined) (pull_request) Successful in 36s
CI / fuzz-build (pull_request) Successful in 16s
CI / coverage (pull_request) Successful in 33s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 33s

This commit is contained in:
2026-09-03 16:03:19 +02:00
parent 190fc5d300
commit eb4e9fba1f
13 changed files with 74 additions and 6 deletions
+1
View File
@@ -68,6 +68,7 @@ static void config_set_defaults(Config* config) {
config->debug_level = 0;
config->list_only = false;
config->human_readable = false;
config->eight_bit_output = false;
config->update = false;
config->inplace = false;
config->append = false;
+1
View File
@@ -74,6 +74,7 @@ typedef struct Config {
int debug_level;
bool list_only;
bool human_readable;
bool eight_bit_output;
// Issue #127: Transfer modes
bool update;
+9
View File
@@ -8,6 +8,7 @@
static const char* log_level_strings[] = {"DEBUG", "INFO", "WARN", "ERROR"};
static LogLevel current_log_level = LOG_LEVEL_WARNING;
static FILE* log_fp = NULL;
static bool eight_bit_output = false;
void set_log_level(LogLevel level) {
current_log_level = level;
@@ -17,6 +18,14 @@ void log_set_file(FILE* fp) {
log_fp = fp;
}
void log_set_8_bit_output(bool enabled) {
eight_bit_output = enabled;
}
bool log_get_8_bit_output(void) {
return eight_bit_output;
}
static inline void write_message(FILE* dest_io, LogLevel log_level, struct tm t, const char* format,
va_list args) {
fprintf(dest_io, "%04d-%02d-%02d %02d:%02d:%02d [%s]: ", t.tm_year + 1900, t.tm_mon + 1,
+3
View File
@@ -2,6 +2,7 @@
#define LOG_H
#include <stdio.h>
#include <stdbool.h>
typedef enum { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR } LogLevel;
@@ -9,5 +10,7 @@ void log_message(LogLevel log_level, const char* message, ...);
void log_perror(const char* context);
void set_log_level(LogLevel level);
void log_set_file(FILE* fp);
void log_set_8_bit_output(bool enabled);
bool log_get_8_bit_output(void);
#endif
+7 -2
View File
@@ -1,5 +1,6 @@
#include "protocol.h"
#include "log.h"
#include "utils.h"
#include <errno.h>
#include <limits.h>
#include <openssl/ssl.h>
@@ -328,7 +329,9 @@ bool protocol_send_str(ProtocolSession* session, const char* data) {
return false;
if (!protocol_send_n_data(session, data, size))
return false;
log_message(LOG_LEVEL_DEBUG, "Send String: %s", data);
char* escaped = output_escape(data, log_get_8_bit_output());
log_message(LOG_LEVEL_DEBUG, "Send String: %s", escaped ? escaped : "<allocation failed>");
free(escaped);
return true;
}
@@ -356,7 +359,9 @@ char* protocol_receive_str(ProtocolSession* session) {
}
data[size] = '\0';
session->total_allocated_bytes += size + 1;
log_message(LOG_LEVEL_DEBUG, "Received String: %s", data);
char* escaped = output_escape(data, log_get_8_bit_output());
log_message(LOG_LEVEL_DEBUG, "Received String: %s", escaped ? escaped : "<allocation failed>");
free(escaped);
return data;
}
+30 -1
View File
@@ -1,6 +1,7 @@
#include "utils.h"
#include "array_list.h"
#include "libgen.h"
#include "log.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -132,6 +133,32 @@ char* str_dup(const char* string) {
return new_string;
}
char* output_escape(const char* string, bool eight_bit_output) {
if (!string)
return NULL;
size_t length = strlen(string);
if (length > (SIZE_MAX - 1) / 5)
return NULL;
char* escaped = malloc(length * 5 + 1);
if (!escaped)
return NULL;
size_t out = 0;
for (size_t i = 0; i < length; i++) {
unsigned char byte = (unsigned char)string[i];
if ((byte >= 32 && byte <= 126) || (eight_bit_output && byte >= 128)) {
escaped[out++] = (char)byte;
} else {
escaped[out++] = '\\';
escaped[out++] = '#';
escaped[out++] = (char)('0' + ((byte >> 6) & 7));
escaped[out++] = (char)('0' + ((byte >> 3) & 7));
escaped[out++] = (char)('0' + (byte & 7));
}
}
escaped[out] = '\0';
return escaped;
}
/* Match a glob pattern against a string. Supported wildcards:
* ? matches any single character except '/'.
* * matches any sequence of characters within one path component (no '/').
@@ -274,7 +301,9 @@ static bool delete_extras_fd(int dirfd, const char* rel_path, ArrayList* manifes
} else {
(*deleted_count)++;
}
fprintf(stderr, " Deleted: %s\n", child_rel);
char* escaped_path = output_escape(child_rel, log_get_8_bit_output());
fprintf(stderr, " Deleted: %s\n", escaped_path ? escaped_path : "<allocation failed>");
free(escaped_path);
}
}
free(child_rel);
+1
View File
@@ -7,6 +7,7 @@
bool mkdir_r(const char* path);
char* str_dup(const char* string);
char* output_escape(const char* string, bool eight_bit_output);
char* path_cat(const char* path1, const char* path2);
bool glob_match(const char* pattern, const char* str);
bool delete_extras(const char* dest_root, ArrayList* manifest);