On linux there is the utility called unix2dos which converts UNIX EOLs(\n) to DOS EOLs(\r\n). However on windows there is no such tool so as a result I decided to make one.
unix2dos.c
:
#include <windows.h>
#include <stdint.h>
#include <stddef.h>
#define chunksize (1 << 13)
#define nullptr ((void *)0)
uint8_t buffer[chunksize + 1] = { 0 };
int64_t newline_count(HANDLE filehandle)
{
DWORD bytes_read = 0;
int64_t result = 0;
do
{
if (ReadFile(filehandle, buffer + 1, chunksize, &bytes_read, nullptr) == 0)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not read file", 26, nullptr, nullptr);
ExitProcess(GetLastError());
}
if (SetFilePointerEx(filehandle, (LARGE_INTEGER) { .QuadPart = -1 }, nullptr, SEEK_CUR) == 0)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not read file", 26, nullptr, nullptr);
ExitProcess(GetLastError());
}
if (ReadFile(filehandle, buffer, 1, nullptr, nullptr) == 0)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not read file", 26, nullptr, nullptr);
ExitProcess(GetLastError());
}
if (SetFilePointerEx(filehandle, (LARGE_INTEGER) { .QuadPart = -1 }, nullptr, SEEK_CUR) == 0)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not read file", 26, nullptr, nullptr);
ExitProcess(GetLastError());
}
for (uint8_t *start = buffer + 1; start != buffer + 1 + (int64_t)bytes_read; ++start)
{
if (start[0] == '\n' && start[-1] != '\r') ++result;
}
} while (bytes_read == chunksize);
return result;
}
void unix2dos1(wchar_t const *const src, wchar_t const *const dst)
{
HANDLE const dst_file = CreateFileW(dst, GENERIC_ALL, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (dst_file == INVALID_HANDLE_VALUE)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not open ", 22, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), dst, lstrlenW(dst), nullptr, nullptr);
ExitProcess(GetLastError());
}
HANDLE const src_file = CreateFileW(src, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (src_file == INVALID_HANDLE_VALUE)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not open ", 22, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), src, lstrlenW(src), nullptr, nullptr);
ExitProcess(GetLastError());
}
int64_t invalid_newline_count = newline_count(src_file);
LARGE_INTEGER end_locaition = { 0 };
if (GetFileSizeEx(src_file, &end_locaition) == 0)
{
CloseHandle(src_file);
CloseHandle(dst_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not get the size of ", 33, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), src, lstrlenW(src), nullptr, nullptr);
ExitProcess(GetLastError());
}
if (SetFilePointerEx(dst_file, (LARGE_INTEGER) { .QuadPart = invalid_newline_count + end_locaition.QuadPart }, &end_locaition, FILE_BEGIN) == 0)
{
CloseHandle(src_file);
CloseHandle(dst_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not resize ", 24, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), dst, lstrlenW(dst), nullptr, nullptr);
ExitProcess(GetLastError());
}
if (SetEndOfFile(dst_file) == 0)
{
CloseHandle(dst_file);
CloseHandle(src_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not resize ", 24, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), dst, lstrlenW(dst), nullptr, nullptr);
ExitProcess(GetLastError());
}
HANDLE const dst_memory_mapped_file = CreateFileMappingW(
dst_file,
nullptr,
PAGE_READWRITE,
0, 0,
nullptr
);
if (dst_memory_mapped_file == nullptr)
{
CloseHandle(src_file);
CloseHandle(dst_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not create file mapping object for ", 48, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), dst, lstrlenW(dst), nullptr, nullptr);
ExitProcess(GetLastError());
}
HANDLE const src_memory_mapped_file = CreateFileMappingW(
src_file,
nullptr,
PAGE_READONLY,
0, 0,
nullptr
);
if (src_memory_mapped_file == nullptr)
{
CloseHandle(dst_memory_mapped_file);
CloseHandle(src_file);
CloseHandle(dst_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not create file mapping object for ", 48, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), src, lstrlenW(src), nullptr, nullptr);
ExitProcess(GetLastError());
}
uint8_t *const src_file_buffer = MapViewOfFile(src_memory_mapped_file, FILE_MAP_READ, 0, 0, end_locaition.QuadPart - invalid_newline_count);
if (src_file_buffer == nullptr)
{
CloseHandle(dst_memory_mapped_file);
CloseHandle(src_memory_mapped_file);
CloseHandle(src_file);
CloseHandle(dst_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not map view of ", 29, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), src, lstrlenW(src), nullptr, nullptr);
ExitProcess(GetLastError());
}
uint8_t *const dst_file_buffer = MapViewOfFile(dst_memory_mapped_file, FILE_MAP_ALL_ACCESS, 0, 0, end_locaition.QuadPart);
if (dst_file_buffer == nullptr)
{
UnmapViewOfFile(src_file_buffer);
CloseHandle(dst_memory_mapped_file);
CloseHandle(src_memory_mapped_file);
CloseHandle(src_file);
CloseHandle(dst_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not map view of ", 29, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), dst, lstrlenW(dst), nullptr, nullptr);
ExitProcess(GetLastError());
}
uint8_t *start1 = src_file_buffer;
uint8_t *start2 = dst_file_buffer;
end_locaition.QuadPart -= invalid_newline_count;
for (; end_locaition.QuadPart; ++start1, ++start2, --end_locaition.QuadPart)
{
if (start1[0] == '\n')
{
if (start1 - 1 <= src_file_buffer || start1[-1] != '\r')
{
*start2++ = '\r';
}
}
start2[0] = start1[0];
}
UnmapViewOfFile(src_file_buffer);
UnmapViewOfFile(dst_file_buffer);
CloseHandle(dst_memory_mapped_file);
CloseHandle(src_memory_mapped_file);
CloseHandle(src_file);
CloseHandle(dst_file);
}
void unix2dos2(const wchar_t *const filepath)
{
HANDLE const file = CreateFileW(filepath, GENERIC_ALL, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE)
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not open ", 22, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), filepath, lstrlenW(filepath), nullptr, nullptr);
ExitProcess(GetLastError());
}
int64_t invalid_newline_count = newline_count(file);
if (invalid_newline_count == 0)
{
CloseHandle(file);
return;
}
LARGE_INTEGER end_locaition = { 0 };
if (SetFilePointerEx(file, (LARGE_INTEGER) { .QuadPart = invalid_newline_count }, &end_locaition, FILE_END) == 0)
{
CloseHandle(file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not resize ", 24, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), filepath, lstrlenW(filepath), nullptr, nullptr);
ExitProcess(GetLastError());
}
if (SetEndOfFile(file) == 0)
{
CloseHandle(file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not resize ", 24, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), filepath, lstrlenW(filepath), nullptr, nullptr);
ExitProcess(GetLastError());
}
HANDLE const memory_mapped_file = CreateFileMappingW(
file,
nullptr,
PAGE_READWRITE,
0, 0,
nullptr
);
if (memory_mapped_file == nullptr)
{
CloseHandle(file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not create file mapping object for ", 48, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), filepath, lstrlenW(filepath), nullptr, nullptr);
ExitProcess(GetLastError());
}
uint8_t *const file_buffer = MapViewOfFile(memory_mapped_file, FILE_MAP_ALL_ACCESS, 0, 0, end_locaition.QuadPart);
if (file_buffer == nullptr)
{
CloseHandle(file);
CloseHandle(memory_mapped_file);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"Error: could not map view of ", 29, nullptr, nullptr);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), filepath, lstrlenW(filepath), nullptr, nullptr);
ExitProcess(GetLastError());
}
uint8_t *start1 = file_buffer + end_locaition.QuadPart - invalid_newline_count - 1;
uint8_t *start2 = file_buffer + end_locaition.QuadPart - 1;
for (; start1 - file_buffer >= 0; --start1, --start2)
{
start2[0] = start1[0];
if (start1[0] == '\n')
{
if (start1 - 1 <= file_buffer || start1[-1] != '\r')
{
*--start2 = '\r';
}
}
}
/* cleanup */
UnmapViewOfFile(file_buffer);
CloseHandle(memory_mapped_file);
CloseHandle(file);
}
void __cdecl mainCRTStartup()
{
int argc;
wchar_t **const argv = CommandLineToArgvW(GetCommandLineW(), &argc) + 1;
--argc;
enum mode
{
mode_overwrite = 0x0,
mode_create_file = 0x1,
} current_mode = { mode_overwrite };
for (int i = 0; i < argc; ++i)
{
if (lstrcmpW(argv[i], L"-o") == 0)
{
current_mode = mode_overwrite;
}
else if (lstrcmpW(argv[i], L"-n") == 0)
{
current_mode = mode_create_file;
}
else
{
switch (current_mode)
{
case mode_overwrite:
unix2dos2(argv[i]);
break;
case mode_create_file:
if (lstrcmpW(argv[i], argv[i + 1]) != 0)
{
unix2dos1(argv[i], argv[i + 1]);
}
else
{
unix2dos2(argv[i]);
}
++i;
break;
}
}
}
/* free memory and exit */
LocalFree(argv - 1);
ExitProcess(0);
}
to build the code use
cl.exe -nologo -Oi -GS -Gs9999999 unix2dos.c -link -subsystem:console -nodefaultlib kernel32.lib shell32.lib -stack:0x100000,0x100000