-
-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(windows): handle CRLF specific to windows #10697
Changes from all commits
1da38a3
3a4095b
4d1cddb
4524724
8cb395c
b263c03
a865eff
c9bd1e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -164,3 +164,125 @@ ContextItemToAppContext(km_core_context_item *contextItems, PWSTR outBuf, DWORD | |||||
delete[] buf; | ||||||
return TRUE; | ||||||
} | ||||||
|
||||||
LBType normalize_line_breaks(LPCWSTR windows_context, LPWSTR core_context, uint32_t core_context_size_in_chars) { | ||||||
|
||||||
// Error Checking | ||||||
if (windows_context == nullptr || core_context == nullptr || core_context_size_in_chars == 0) { | ||||||
return lbERROR; | ||||||
} | ||||||
auto windows_context_length = wcsnlen_s(windows_context, MAXCONTEXT); | ||||||
|
||||||
if (core_context_size_in_chars <= windows_context_length) { | ||||||
return lbERROR; | ||||||
} | ||||||
|
||||||
// Replace all line breaks with LF and determine line break type | ||||||
LBType match = lbNONE; | ||||||
LPCWSTR win_ptr = windows_context; | ||||||
LPWSTR core_ptr = core_context; | ||||||
while (*win_ptr != L'\0') { | ||||||
if (*win_ptr == L'\r' && *(win_ptr + 1) == L'\n') { | ||||||
win_ptr++; // skip '\r' | ||||||
*core_ptr++ = *win_ptr++; | ||||||
match = lbCRLF; | ||||||
} else if (*win_ptr == L'\r') { | ||||||
*core_ptr++ = L'\n'; | ||||||
win_ptr++; | ||||||
match = lbCR; | ||||||
} else if (*win_ptr == L'\n') { | ||||||
*core_ptr++ = *win_ptr++; | ||||||
match = lbLF; | ||||||
} | ||||||
else { | ||||||
*core_ptr++ = *win_ptr++; | ||||||
} | ||||||
} | ||||||
*core_ptr = L'\0'; | ||||||
|
||||||
return match; | ||||||
} | ||||||
|
||||||
BOOL | ||||||
restore_line_breaks(LPWSTR win_out_str, uint32_t win_out_size_in_chars, LBType line_break, LBType default_lb ){ | ||||||
if (win_out_str == nullptr) { | ||||||
return rsERROR; | ||||||
} | ||||||
|
||||||
if (line_break == lbNONE){ | ||||||
line_break = default_lb; | ||||||
} | ||||||
|
||||||
// return early if doesn't contain any '\n'; | ||||||
if (wcsstr(win_out_str, L"\n") == nullptr) { | ||||||
return rsNO_LB; | ||||||
} | ||||||
|
||||||
size_t buf_length = wcslen(win_out_str) * 2; | ||||||
LPWSTR buf_string = new WCHAR[buf_length]; | ||||||
|
||||||
LPCWSTR in_ptr = win_out_str; | ||||||
LPWSTR buf_ptr = buf_string; | ||||||
|
||||||
while (*in_ptr != L'\0') { | ||||||
if (*in_ptr == '\n') { | ||||||
Comment on lines
+227
to
+228
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not consistently using |
||||||
switch (line_break) { | ||||||
case lbLF: | ||||||
*buf_ptr++ = *in_ptr++; | ||||||
break; | ||||||
case lbCRLF: | ||||||
*buf_ptr++ = '\r'; | ||||||
*buf_ptr++ = *in_ptr++; | ||||||
break; | ||||||
case lbCR: | ||||||
*buf_ptr++ = '\r'; | ||||||
*in_ptr++; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
break; | ||||||
} | ||||||
} else { | ||||||
*buf_ptr++ = *in_ptr++; | ||||||
} | ||||||
} | ||||||
*buf_ptr = '\0'; // Null terminate the modified string | ||||||
|
||||||
// may now need to truncate the string preserving the end closest the caret. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of doing this, couldn't we just return the longer buffer? |
||||||
auto final_length = wcsnlen_s(buf_string, buf_length); | ||||||
if (final_length < win_out_size_in_chars) { | ||||||
wcscpy_s(win_out_str, win_out_size_in_chars, buf_string); | ||||||
} else { | ||||||
auto diff = final_length + 1 - win_out_size_in_chars; // +1 for null termination | ||||||
wcscpy_s(win_out_str, win_out_size_in_chars, buf_string + diff); | ||||||
Comment on lines
+253
to
+254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could break a surrogate pair |
||||||
} | ||||||
delete[] buf_string; | ||||||
return rsSUCCESS; | ||||||
} | ||||||
|
||||||
BOOL | ||||||
context_char32_char16(const km_core_usv *core_output, LPWSTR win_out_str, uint32_t win_output_size_in_char) { | ||||||
if (core_output == nullptr || win_out_str == nullptr || win_output_size_in_char <= 0) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
uint32_t cannot be <0 |
||||||
return FALSE; | ||||||
} | ||||||
uint8_t idx = 0; | ||||||
size_t buf_length = (MAXCONTEXT * 2) + 1; | ||||||
WCHAR *buf = new WCHAR[(MAXCONTEXT * 2) + 1]; // if every character is a surrogate | ||||||
while (*core_output) { | ||||||
if (Uni_IsSMP(*core_output)) { | ||||||
buf[idx++] = static_cast<WCHAR> Uni_UTF32ToSurrogate1(*core_output); | ||||||
buf[idx++] = static_cast<WCHAR> Uni_UTF32ToSurrogate2(*core_output); | ||||||
} else { | ||||||
buf[idx++] = static_cast<WCHAR>(*core_output); | ||||||
} | ||||||
core_output++; | ||||||
} | ||||||
buf[idx] = 0; // Null terminate character array | ||||||
|
||||||
auto final_length = wcsnlen_s(buf, buf_length); | ||||||
if (final_length < win_output_size_in_char) { | ||||||
wcscpy_s(win_out_str, win_output_size_in_char, buf); | ||||||
} else { | ||||||
auto diff = final_length + 1 - win_output_size_in_char; // +1 for null termination | ||||||
wcscpy_s(win_out_str, win_output_size_in_char, buf + diff); | ||||||
} | ||||||
Comment on lines
+279
to
+285
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same risk of splitting a surrogate pair |
||||||
delete[] buf; | ||||||
return TRUE; | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,3 +78,58 @@ TEST(AppContext, AppContext_Delete) { | |
testContext.Delete(); | ||
|
||
} | ||
|
||
// Test normalize and restore line break functions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need some tests for surrogate pair edge cases, especially boundary conditions |
||
|
||
TEST(AppContext, normalize_restore_CRLF) { | ||
wchar_t windows_context[] = L"Hello\r\nWorld\r\nYou Rock"; | ||
wchar_t core_context[MAXCONTEXT]; | ||
wchar_t expected_string[] = L"Hello\nWorld\nYou Rock"; | ||
LBType lb_type; | ||
lb_type = normalize_line_breaks(windows_context, core_context, MAXCONTEXT); | ||
EXPECT_TRUE(lb_type == lbCRLF); | ||
EXPECT_STREQ(expected_string, core_context); | ||
EXPECT_TRUE(restore_line_breaks(core_context, MAXCONTEXT, lb_type, lbCRLF) == rsSUCCESS); | ||
// should be the same as the original windows_context. | ||
EXPECT_STREQ(windows_context, core_context); | ||
} | ||
|
||
// No CRLF `\r\n` found | ||
TEST(AppContext, normalize_restore_NONE) { | ||
wchar_t windows_context[] = L"Hello World You Rock"; | ||
wchar_t core_context[MAXCONTEXT]; | ||
wchar_t expected_string[] = L"Hello World You Rock"; | ||
LBType lb_type; | ||
lb_type = normalize_line_breaks(windows_context, core_context, MAXCONTEXT); | ||
EXPECT_TRUE(lb_type == lbNONE); | ||
EXPECT_STREQ(expected_string, core_context); | ||
EXPECT_TRUE(restore_line_breaks(core_context, MAXCONTEXT, lb_type, lbCRLF) == rsNO_LB); | ||
// should be the same as the original windows_context. | ||
EXPECT_STREQ(windows_context, core_context); | ||
} | ||
|
||
TEST(AppContext, normalize_restore_CR) { | ||
wchar_t windows_context[] = L"Hello\rWorld\rYou Rock"; | ||
wchar_t core_context[MAXCONTEXT]; | ||
wchar_t expected_string[] = L"Hello\nWorld\nYou Rock"; | ||
LBType lb_type; | ||
lb_type = normalize_line_breaks(windows_context, core_context, MAXCONTEXT); | ||
EXPECT_TRUE(lb_type == lbCR); | ||
EXPECT_STREQ(expected_string, core_context); | ||
EXPECT_TRUE(restore_line_breaks(core_context, MAXCONTEXT, lb_type, lbCRLF) == rsSUCCESS); | ||
// should be the same as the original windows_context. | ||
EXPECT_STREQ(windows_context, core_context); | ||
} | ||
|
||
TEST(AppContext, normalize_restore_LF) { | ||
wchar_t windows_context[] = L"Hello\nWorld\nYou Rock"; | ||
wchar_t core_context[MAXCONTEXT]; | ||
wchar_t expected_string[] = L"Hello\nWorld\nYou Rock"; | ||
LBType lb_type; | ||
lb_type = normalize_line_breaks(windows_context, core_context, MAXCONTEXT); | ||
EXPECT_TRUE(lb_type == lbLF); | ||
EXPECT_STREQ(expected_string, core_context); | ||
EXPECT_TRUE(restore_line_breaks(core_context, MAXCONTEXT, lb_type, lbCRLF) == rsSUCCESS); | ||
// should be the same as the original windows_context. | ||
EXPECT_STREQ(windows_context, core_context); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't allow space for final terminating nul if every char is doubled