Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ AC_CHECK_HEADERS(poll.h sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h
sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \
popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netgroup.h \
zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h \
sys/resource.h bsd/string.h idn2.h)
sys/resource.h bsd/string.h idn2.h wchar.h)
AC_CHECK_HEADERS([netinet/ip.h], [], [], [[#include <netinet/in.h>]])
AC_HEADER_MAJOR_FIXED

Expand Down Expand Up @@ -978,7 +978,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd chown chmod lchmod mknod mkfifo fdopendir \
seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
initgroups utimensat futimens posix_fallocate attropen setvbuf nanosleep usleep \
setenv unsetenv)
setenv unsetenv mbrtowc)

dnl dirfd() is a macro or static inline on several systems (the BSDs), so the
dnl default AC_CHECK_FUNCS link probe -- which declares `char dirfd(void);` and
Expand Down
3 changes: 2 additions & 1 deletion flist.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ static void emit_filelist_progress(int count)
return;
if (output_needs_newline == 2) /* avoid a newline in the middle of this filelist-progress output */
output_needs_newline = 0;
rprintf(FCLIENT, " %d files...\r", count);
rprintf(FCLIENT, " %d files...", count);
rput_progress();
output_needs_newline = 2;
}

Expand Down
6 changes: 4 additions & 2 deletions generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,10 @@ static void do_delete_pass(void)
}
delete_in_dir(NULL, NULL, dev_zero);

if (INFO_GTE(FLIST, 2) && !am_server)
rprintf(FINFO, " \r");
if (INFO_GTE(FLIST, 2) && !am_server) {
rprintf(FINFO, " ");
rput_progress();
}
}

static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file)
Expand Down
151 changes: 118 additions & 33 deletions log.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
#include "inums.h"
#include "rounding.h" /* EXTRA_ROUNDING, so log_delete() aligns its file_struct */

#if defined HAVE_WCHAR_H && defined HAVE_MBRTOWC
#include <wchar.h>
typedef mbstate_t filter_mbstate;
#else
typedef int filter_mbstate;
#endif

extern int dry_run;
extern int am_daemon;
extern int am_server;
Expand Down Expand Up @@ -120,7 +127,7 @@ static char const *rerr_name(int code)
return NULL;
}

static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, int escape_c1, char end_char);
static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, char end_char);

static void logit(int priority, const char *buf)
{
Expand All @@ -131,9 +138,9 @@ static void logit(int priority, const char *buf)
* filename can't inject terminal escapes into the log an admin later
* cat's (CWE-117); keep the trailing newline raw via end_char. */
int len = strlen(buf);
char trailing = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';
char trailing = len && buf[len-1] == '\n' ? buf[--len] : '\0';
fprintf(logfile_fp, "%s [%d] ", timestring(time(NULL)), (int)getpid());
filtered_fwrite(logfile_fp, buf, len, 0, 1, trailing);
filtered_fwrite(logfile_fp, buf, len, 0, trailing);
fflush(logfile_fp);
} else {
syslog(priority, "%s", buf);
Expand Down Expand Up @@ -239,27 +246,100 @@ void logfile_reopen(void)
}
}

static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, int escape_c1, char end_char)
/* Decode one character using the active locale. Invalid or incomplete input
* returns 0 so its bytes are filtered individually. */
static size_t locale_char_len(const char *buf, size_t len, filter_mbstate *state,
int *is_control)
{
#if defined HAVE_WCHAR_H && defined HAVE_MBRTOWC
wchar_t wc;
size_t char_len = mbrtowc(&wc, buf, len, state);

if (char_len == (size_t)-1 || char_len == (size_t)-2) {
memset(state, 0, sizeof *state);
return 0;
}
if (char_len == 0)
char_len = 1;
*is_control = wc != L'\t'
&& (wc < L' ' || (wc >= 0x7f && wc <= 0x9f));
return char_len;
#else
(void)buf;
(void)len;
(void)state;
(void)is_control;
return 0;
#endif
}

static size_t filtered_char_len(const char *buf, size_t len, int use_isprint,
filter_mbstate *state, int *escape)
{
char outbuf[1024], *ob = outbuf;
uchar byte = *(const uchar *)buf;
int is_control = 0;
size_t char_len = !use_isprint && byte >= 0x80
Comment thread
steadytao marked this conversation as resolved.
? locale_char_len(buf, len, state, &is_control) : 0;

if (char_len) {
*escape = is_control;
return char_len;
}
*escape = (len > 4 && *buf == '\\' && buf[1] == '#'
&& isDigit(buf + 2) && isDigit(buf + 3) && isDigit(buf + 4))
|| (*buf != '\t' && ((use_isprint && !isPrint(buf)) || byte < ' '
|| byte == 0x7f || (byte >= 0x80 && byte <= 0x9f)));
Comment thread
steadytao marked this conversation as resolved.
return 1;
}

static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, char end_char)
{
char outbuf[1024];
filter_mbstate state;
size_t out_len = 0;
const char *end = in_buf + in_len;
memset(&state, 0, sizeof state);

while (in_buf < end) {
if (ob - outbuf >= (int)sizeof outbuf - 10) {
if (fwrite(outbuf, ob - outbuf, 1, f) != 1)
int escape;
size_t i, char_len = filtered_char_len(
in_buf, (size_t)(end - in_buf), use_isprint, &state, &escape);
size_t expansion = escape ? 5 : 1;
size_t out_size;

if (out_len > sizeof outbuf || char_len > sizeof outbuf / expansion)
exit_cleanup(RERR_MESSAGEIO);
out_size = char_len * expansion;
if (sizeof outbuf - out_len < out_size) {
if (out_len && fwrite(outbuf, 1, out_len, f) != out_len)
exit_cleanup(RERR_MESSAGEIO);
ob = outbuf;
out_len = 0;
}
if ((in_buf < end - 4 && *in_buf == '\\' && in_buf[1] == '#'
&& isDigit(in_buf + 2) && isDigit(in_buf + 3) && isDigit(in_buf + 4))
|| (*in_buf != '\t' && ((use_isprint && !isPrint(in_buf)) || *(uchar*)in_buf < ' '
|| (escape_c1 && *(uchar*)in_buf >= 0x80 && *(uchar*)in_buf <= 0x9f))))
ob += snprintf(ob, 6, "\\#%03o", *(uchar*)in_buf++);
else
*ob++ = *in_buf++;
if (escape) {
for (i = 0; i < char_len; i++) {
uchar byte = (uchar)in_buf[i];
outbuf[out_len++] = '\\';
outbuf[out_len++] = '#';
outbuf[out_len++] = (char)('0' + ((byte >> 6) & 7));
outbuf[out_len++] = (char)('0' + ((byte >> 3) & 7));
outbuf[out_len++] = (char)('0' + (byte & 7));
}
} else {
memcpy(outbuf + out_len, in_buf, char_len);
out_len += char_len;
}
in_buf += char_len;
}
if (end_char) /* The "- 10" above means that there is always room for one more char here. */
*ob++ = end_char;
if (ob != outbuf && fwrite(outbuf, ob - outbuf, 1, f) != 1)
if (end_char && out_len == sizeof outbuf) {
if (fwrite(outbuf, 1, out_len, f) != out_len)
exit_cleanup(RERR_MESSAGEIO);
out_len = 0;
}
if (end_char)
outbuf[out_len++] = end_char;
if (out_len > sizeof outbuf)
exit_cleanup(RERR_MESSAGEIO);
if (out_len && fwrite(outbuf, 1, out_len, f) != out_len)
exit_cleanup(RERR_MESSAGEIO);
}

Expand All @@ -268,7 +348,7 @@ static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isp
* can happen with certain fatal conditions. */
void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
{
char trailing_CR_or_NL;
char trailing_NL;
FILE *f = msgs2stderr == 1 ? stderr : stdout;
#ifdef ICONV_OPTION
iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
Expand Down Expand Up @@ -377,13 +457,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
output_needs_newline = 0;
}

trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';

if (len && buf[0] == '\r') {
fputc('\r', f);
buf++;
len--;
}
trailing_NL = len && buf[len-1] == '\n' ? buf[--len] : '\0';

#ifdef ICONV_CONST
if (ic != (iconv_t)-1) {
Expand All @@ -398,10 +472,10 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
iconvbufs(ic, &inbuf, &outbuf, inbuf.pos ? 0 : ICB_INIT);
ierrno = errno;
if (outbuf.len) {
char trailing = inbuf.len ? '\0' : trailing_CR_or_NL;
filtered_fwrite(f, convbuf, outbuf.len, 0, 0, trailing);
char trailing = inbuf.len ? '\0' : trailing_NL;
filtered_fwrite(f, convbuf, outbuf.len, 0, trailing);
if (trailing) {
trailing_CR_or_NL = '\0';
trailing_NL = '\0';
fflush(f);
}
outbuf.len = 0;
Expand All @@ -415,19 +489,30 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
}
}

if (trailing_CR_or_NL) {
fputc(trailing_CR_or_NL, f);
if (trailing_NL) {
fputc(trailing_NL, f);
fflush(f);
}
} else
#endif
{
filtered_fwrite(f, buf, len, !allow_8bit_chars, 0, trailing_CR_or_NL);
if (trailing_CR_or_NL)
filtered_fwrite(f, buf, len, !allow_8bit_chars, trailing_NL);
if (trailing_NL)
fflush(f);
}
}

/* Emit the carriage return owned by rsync's progress displays. */
void rput_progress(void)
{
FILE *f = msgs2stderr == 1 ? stderr : stdout;

if (quiet)
return;
if (fputc('\r', f) == EOF || fflush(f) == EOF)
exit_cleanup(RERR_MESSAGEIO);
}

/* This is the rsync debugging function. Call it with FINFO, FERROR_*,
* FWARNING, FLOG, or FCLIENT. */
void rprintf(enum logcode code, const char *format, ...)
Expand Down
3 changes: 2 additions & 1 deletion progress.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_l

output_needs_newline = 0;
pct = ofs == size ? 100 : (int) (100.0 * ofs / size);
rprintf(FCLIENT, "\r%15s %3d%% %7.2f%s %s%s",
rput_progress();
rprintf(FCLIENT, "%15s %3d%% %7.2f%s %s%s",
human_num(ofs), pct, rate, units, rembuf, eol);
if (!is_last && !quiet) {
output_needs_newline = 1;
Expand Down
Loading
Loading