Fixes many rfc2047 quoting issues in the output from format-patch.

* js/format-2047:
  format-patch tests: check quoting/encoding in To: and Cc: headers
  format-patch: fix rfc2047 address encoding with respect to rfc822 specials
  format-patch: make rfc2047 encoding more strict
  format-patch: introduce helper function last_line_length()
  format-patch: do not wrap rfc2047 encoded headers too late
  format-patch: do not wrap non-rfc2047 headers too early
  utf8: fix off-by-one wrapping of text
This commit is contained in:
Jeff King 2012-11-09 12:42:32 -05:00
Родитель 15ba878a1d 25dc8dad3a
Коммит 64b22a5894
5 изменённых файлов: 263 добавлений и 127 удалений

Просмотреть файл

@ -506,6 +506,7 @@ extern const char tolower_trans_tbl[256];
#undef isdigit
#undef isalpha
#undef isalnum
#undef isprint
#undef islower
#undef isupper
#undef tolower
@ -523,6 +524,7 @@ extern unsigned char sane_ctype[256];
#define isdigit(x) sane_istest(x,GIT_DIGIT)
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
#define isprint(x) ((x) >= 0x20 && (x) <= 0x7e)
#define islower(x) sane_iscase(x, 1)
#define isupper(x) sane_iscase(x, 0)
#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)

149
pretty.c
Просмотреть файл

@ -231,7 +231,7 @@ static int is_rfc822_special(char ch)
}
}
static int has_rfc822_specials(const char *s, int len)
static int needs_rfc822_quoting(const char *s, int len)
{
int i;
for (i = 0; i < len; i++)
@ -240,6 +240,17 @@ static int has_rfc822_specials(const char *s, int len)
return 0;
}
static int last_line_length(struct strbuf *sb)
{
int i;
/* How many bytes are already used on the last line? */
for (i = sb->len - 1; i >= 0; i--)
if (sb->buf[i] == '\n')
break;
return sb->len - (i + 1);
}
static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
{
int i;
@ -261,57 +272,110 @@ static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
strbuf_addch(out, '"');
}
static int is_rfc2047_special(char ch)
enum rfc2047_type {
RFC2047_SUBJECT,
RFC2047_ADDRESS,
};
static int is_rfc2047_special(char ch, enum rfc2047_type type)
{
return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
/*
* rfc2047, section 4.2:
*
* 8-bit values which correspond to printable ASCII characters other
* than "=", "?", and "_" (underscore), MAY be represented as those
* characters. (But see section 5 for restrictions.) In
* particular, SPACE and TAB MUST NOT be represented as themselves
* within encoded words.
*/
/*
* rule out non-ASCII characters and non-printable characters (the
* non-ASCII check should be redundant as isprint() is not localized
* and only knows about ASCII, but be defensive about that)
*/
if (non_ascii(ch) || !isprint(ch))
return 1;
/*
* rule out special printable characters (' ' should be the only
* whitespace character considered printable, but be defensive and use
* isspace())
*/
if (isspace(ch) || ch == '=' || ch == '?' || ch == '_')
return 1;
/*
* rfc2047, section 5.3:
*
* As a replacement for a 'word' entity within a 'phrase', for example,
* one that precedes an address in a From, To, or Cc header. The ABNF
* definition for 'phrase' from RFC 822 thus becomes:
*
* phrase = 1*( encoded-word / word )
*
* In this case the set of characters that may be used in a "Q"-encoded
* 'encoded-word' is restricted to: <upper and lower case ASCII
* letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_"
* (underscore, ASCII 95.)>. An 'encoded-word' that appears within a
* 'phrase' MUST be separated from any adjacent 'word', 'text' or
* 'special' by 'linear-white-space'.
*/
if (type != RFC2047_ADDRESS)
return 0;
/* '=' and '_' are special cases and have been checked above */
return !(isalnum(ch) || ch == '!' || ch == '*' || ch == '+' || ch == '-' || ch == '/');
}
static void add_rfc2047(struct strbuf *sb, const char *line, int len,
const char *encoding)
static int needs_rfc2047_encoding(const char *line, int len,
enum rfc2047_type type)
{
static const int max_length = 78; /* per rfc2822 */
int i;
int line_len;
/* How many bytes are already used on the current line? */
for (i = sb->len - 1; i >= 0; i--)
if (sb->buf[i] == '\n')
break;
line_len = sb->len - (i+1);
for (i = 0; i < len; i++) {
int ch = line[i];
if (non_ascii(ch) || ch == '\n')
goto needquote;
return 1;
if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
goto needquote;
return 1;
}
strbuf_add_wrapped_bytes(sb, line, len, 0, 1, max_length - line_len);
return;
needquote:
return 0;
}
static void add_rfc2047(struct strbuf *sb, const char *line, int len,
const char *encoding, enum rfc2047_type type)
{
static const int max_encoded_length = 76; /* per rfc2047 */
int i;
int line_len = last_line_length(sb);
strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
strbuf_addf(sb, "=?%s?q?", encoding);
line_len += strlen(encoding) + 5; /* 5 for =??q? */
for (i = 0; i < len; i++) {
unsigned ch = line[i] & 0xFF;
int is_special = is_rfc2047_special(ch, type);
if (line_len >= max_length - 2) {
/*
* According to RFC 2047, we could encode the special character
* ' ' (space) with '_' (underscore) for readability. But many
* programs do not understand this and just leave the
* underscore in place. Thus, we do nothing special here, which
* causes ' ' to be encoded as '=20', avoiding this problem.
*/
if (line_len + 2 + (is_special ? 3 : 1) > max_encoded_length) {
strbuf_addf(sb, "?=\n =?%s?q?", encoding);
line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
}
/*
* We encode ' ' using '=20' even though rfc2047
* allows using '_' for readability. Unfortunately,
* many programs do not understand this and just
* leave the underscore in place.
*/
if (is_rfc2047_special(ch) || ch == ' ' || ch == '\n') {
if (is_special) {
strbuf_addf(sb, "=%02X", ch);
line_len += 3;
}
else {
} else {
strbuf_addch(sb, ch);
line_len++;
}
@ -323,6 +387,7 @@ void pp_user_info(const struct pretty_print_context *pp,
const char *what, struct strbuf *sb,
const char *line, const char *encoding)
{
int max_length = 78; /* per rfc2822 */
char *date;
int namelen;
unsigned long time;
@ -340,25 +405,27 @@ void pp_user_info(const struct pretty_print_context *pp,
if (pp->fmt == CMIT_FMT_EMAIL) {
char *name_tail = strchr(line, '<');
int display_name_length;
int final_line;
if (!name_tail)
return;
while (line < name_tail && isspace(name_tail[-1]))
name_tail--;
display_name_length = name_tail - line;
strbuf_addstr(sb, "From: ");
if (!has_rfc822_specials(line, display_name_length)) {
add_rfc2047(sb, line, display_name_length, encoding);
} else {
if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
add_rfc2047(sb, line, display_name_length,
encoding, RFC2047_ADDRESS);
max_length = 76; /* per rfc2047 */
} else if (needs_rfc822_quoting(line, display_name_length)) {
struct strbuf quoted = STRBUF_INIT;
add_rfc822_quoted(&quoted, line, display_name_length);
add_rfc2047(sb, quoted.buf, quoted.len, encoding);
strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
-6, 1, max_length);
strbuf_release(&quoted);
} else {
strbuf_add_wrapped_bytes(sb, line, display_name_length,
-6, 1, max_length);
}
for (final_line = 0; final_line < sb->len; final_line++)
if (sb->buf[sb->len - final_line - 1] == '\n')
break;
if (namelen - display_name_length + final_line > 78) {
if (namelen - display_name_length + last_line_length(sb) > max_length) {
strbuf_addch(sb, '\n');
if (!isspace(name_tail[0]))
strbuf_addch(sb, ' ');
@ -1278,6 +1345,7 @@ void pp_title_line(const struct pretty_print_context *pp,
const char *encoding,
int need_8bit_cte)
{
static const int max_length = 78; /* per rfc2047 */
struct strbuf title;
strbuf_init(&title, 80);
@ -1287,7 +1355,12 @@ void pp_title_line(const struct pretty_print_context *pp,
strbuf_grow(sb, title.len + 1024);
if (pp->subject) {
strbuf_addstr(sb, pp->subject);
add_rfc2047(sb, title.buf, title.len, encoding);
if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT))
add_rfc2047(sb, title.buf, title.len,
encoding, RFC2047_SUBJECT);
else
strbuf_add_wrapped_bytes(sb, title.buf, title.len,
-last_line_length(sb), 1, max_length);
} else {
strbuf_addbuf(sb, &title);
}

Просмотреть файл

@ -110,73 +110,107 @@ test_expect_success 'replay did not screw up the log message' '
test_expect_success 'extra headers' '
git config format.headers "To: R. E. Cipient <rcipient@example.com>
git config format.headers "To: R E Cipient <rcipient@example.com>
" &&
git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
git config --add format.headers "Cc: S E Cipient <scipient@example.com>
" &&
git format-patch --stdout master..side > patch2 &&
sed -e "/^\$/q" patch2 > hdrs2 &&
grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 &&
grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2
'
test_expect_success 'extra headers without newlines' '
git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
git config --add format.headers "Cc: S E Cipient <scipient@example.com>" &&
git format-patch --stdout master..side >patch3 &&
sed -e "/^\$/q" patch3 > hdrs3 &&
grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 &&
grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3
'
test_expect_success 'extra headers with multiple To:s' '
git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
git config --add format.headers "To: S E Cipient <scipient@example.com>" &&
git format-patch --stdout master..side > patch4 &&
sed -e "/^\$/q" patch4 > hdrs4 &&
grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 &&
grep "^ *S E Cipient <scipient@example.com>\$" hdrs4
'
test_expect_success 'additional command line cc' '
test_expect_success 'additional command line cc (ascii)' '
git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
grep "^ *S E Cipient <scipient@example.com>\$" patch5
'
test_expect_failure 'additional command line cc (rfc822)' '
git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
grep "^ *"S. E. Cipient" <scipient@example.com>\$" patch5
'
test_expect_success 'command line headers' '
git config --unset-all format.headers &&
git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6
'
test_expect_success 'configuration headers and command line headers' '
git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 &&
grep "^ *S E Cipient <scipient@example.com>\$" patch7
'
test_expect_success 'command line To: header' '
test_expect_success 'command line To: header (ascii)' '
git config --unset-all format.headers &&
git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
grep "^To: R E Cipient <rcipient@example.com>\$" patch8
'
test_expect_success 'configuration To: header' '
test_expect_failure 'command line To: header (rfc822)' '
git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
grep "^To: "R. E. Cipient" <rcipient@example.com>\$" patch8
'
test_expect_failure 'command line To: header (rfc2047)' '
git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8
'
test_expect_success 'configuration To: header (ascii)' '
git config format.to "R E Cipient <rcipient@example.com>" &&
git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
grep "^To: R E Cipient <rcipient@example.com>\$" patch9
'
test_expect_failure 'configuration To: header (rfc822)' '
git config format.to "R. E. Cipient <rcipient@example.com>" &&
git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
grep "^To: "R. E. Cipient" <rcipient@example.com>\$" patch9
'
test_expect_failure 'configuration To: header (rfc2047)' '
git config format.to "R Ä Cipient <rcipient@example.com>" &&
git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9
'
# check_patch <patch>: Verify that <patch> looks like a half-sane
@ -190,11 +224,11 @@ check_patch () {
test_expect_success '--no-to overrides config.to' '
git config --replace-all format.to \
"R. E. Cipient <rcipient@example.com>" &&
"R E Cipient <rcipient@example.com>" &&
git format-patch --no-to --stdout master..side |
sed -e "/^\$/q" >patch10 &&
check_patch patch10 &&
! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
! grep "^To: R E Cipient <rcipient@example.com>\$" patch10
'
test_expect_success '--no-to and --to replaces config.to' '
@ -212,21 +246,21 @@ test_expect_success '--no-to and --to replaces config.to' '
test_expect_success '--no-cc overrides config.cc' '
git config --replace-all format.cc \
"C. E. Cipient <rcipient@example.com>" &&
"C E Cipient <rcipient@example.com>" &&
git format-patch --no-cc --stdout master..side |
sed -e "/^\$/q" >patch12 &&
check_patch patch12 &&
! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12
'
test_expect_success '--no-add-header overrides config.headers' '
git config --replace-all format.headers \
"Header1: B. E. Cipient <rcipient@example.com>" &&
"Header1: B E Cipient <rcipient@example.com>" &&
git format-patch --no-add-header --stdout master..side |
sed -e "/^\$/q" >patch13 &&
check_patch patch13 &&
! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13
'
test_expect_success 'multiple files' '
@ -752,16 +786,14 @@ M64=$M8$M8$M8$M8$M8$M8$M8$M8
M512=$M64$M64$M64$M64$M64$M64$M64$M64
cat >expect <<'EOF'
Subject: [PATCH] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo
bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar
EOF
test_expect_success 'format-patch wraps extremely long headers (ascii)' '
test_expect_success 'format-patch wraps extremely long subject (ascii)' '
echo content >>file &&
git add file &&
git commit -m "$M512" &&
@ -774,30 +806,31 @@ M8="föö bar "
M64=$M8$M8$M8$M8$M8$M8$M8$M8
M512=$M64$M64$M64$M64$M64$M64$M64$M64
cat >expect <<'EOF'
Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
=?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
=?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
=?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
=?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
=?UTF-8?q?=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3?=
=?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
=?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
=?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
=?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
=?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
=?UTF-8?q?=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3?=
=?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
=?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
=?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?=
=?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?=
=?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3?=
=?UTF-8?q?=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?=
=?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3?=
=?UTF-8?q?=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?=
=?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?=
EOF
test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
rm -rf patches/ &&
echo content >>file &&
git add file &&
@ -807,52 +840,80 @@ test_expect_success 'format-patch wraps extremely long headers (rfc2047)' '
test_cmp expect subject
'
M8="foo_bar_"
M64=$M8$M8$M8$M8$M8$M8$M8$M8
cat >expect <<EOF
From: $M64
<foobar@foo.bar>
EOF
test_expect_success 'format-patch wraps non-quotable headers' '
rm -rf patches/ &&
echo content >>file &&
git add file &&
git commit -mfoo --author "$M64 <foobar@foo.bar>" &&
git format-patch --stdout -1 >patch &&
sed -n "/^From: /p; /^ /p; /^$/q" <patch >from &&
test_cmp expect from
'
check_author() {
echo content >>file &&
git add file &&
GIT_AUTHOR_NAME=$1 git commit -m author-check &&
git format-patch --stdout -1 >patch &&
grep ^From: patch >actual &&
sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual &&
test_cmp expect actual
}
cat >expect <<'EOF'
From: "Foo B. Bar" <author@example.com>
EOF
test_expect_success 'format-patch quotes dot in headers' '
test_expect_success 'format-patch quotes dot in from-headers' '
check_author "Foo B. Bar"
'
cat >expect <<'EOF'
From: "Foo \"The Baz\" Bar" <author@example.com>
EOF
test_expect_success 'format-patch quotes double-quote in headers' '
test_expect_success 'format-patch quotes double-quote in from-headers' '
check_author "Foo \"The Baz\" Bar"
'
cat >expect <<'EOF'
From: =?UTF-8?q?"F=C3=B6o=20B.=20Bar"?= <author@example.com>
From: =?UTF-8?q?F=C3=B6o=20Bar?= <author@example.com>
EOF
test_expect_success 'rfc2047-encoded headers also double-quote 822 specials' '
test_expect_success 'format-patch uses rfc2047-encoded from-headers when necessary' '
check_author "Föo Bar"
'
cat >expect <<'EOF'
From: =?UTF-8?q?F=C3=B6o=20B=2E=20Bar?= <author@example.com>
EOF
test_expect_success 'rfc2047-encoded from-headers leave no rfc822 specials' '
check_author "Föo B. Bar"
'
cat >expect <<EOF
From: foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_
<author@example.com>
EOF
test_expect_success 'format-patch wraps moderately long from-header (ascii)' '
check_author "foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_"
'
cat >expect <<'EOF'
From: Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar
Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo
Bar Foo Bar Foo Bar Foo Bar <author@example.com>
EOF
test_expect_success 'format-patch wraps extremely long from-header (ascii)' '
check_author "Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
'
cat >expect <<'EOF'
From: "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar
Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo
Bar Foo Bar Foo Bar Foo Bar" <author@example.com>
EOF
test_expect_success 'format-patch wraps extremely long from-header (rfc822)' '
check_author "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
'
cat >expect <<'EOF'
From: =?UTF-8?q?Fo=C3=B6=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo?=
=?UTF-8?q?=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20?=
=?UTF-8?q?Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar?=
=?UTF-8?q?=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20?=
=?UTF-8?q?Foo=20Bar=20Foo=20Bar?= <author@example.com>
EOF
test_expect_success 'format-patch wraps extremely long from-header (rfc2047)' '
check_author "Foö Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar"
'
cat >expect <<'EOF'
Subject: header with . in it
EOF

Просмотреть файл

@ -72,9 +72,9 @@ cat > expect << EOF
commit.
EOF
test_expect_success 'format %w(12,1,2)' '
test_expect_success 'format %w(11,1,2)' '
git log -2 --format="%w(12,1,2)This is the %s commit." > actual &&
git log -2 --format="%w(11,1,2)This is the %s commit." > actual &&
test_cmp expect actual
'

2
utf8.c
Просмотреть файл

@ -353,7 +353,7 @@ retry:
c = *text;
if (!c || isspace(c)) {
if (w < width || !space) {
if (w <= width || !space) {
const char *start = bol;
if (!c && text == start)
return w;