/* eol.c: End Of Line - convert line endings. Jason Hood, 21 & 22 June, 2011. Public Domain. Read a file, writing it back with the chosen line ending. v1.01, 5 May, 2014: - fixed severe bug where unchanged files would be copied to next changed. Build (VC): cl /nologo /W3 /O2 /MD eol.c /link /filealign:512 */ #define PVERS L"1.01" #define PDATE L"5 May, 2014" #define UNICODE #define WIN32_LEAN_AND_MEAN #include #include #include #include // VC6 MSVCRT.LIB is missing __wsetargv, so linking wsetargv.obj doesn't work. // Fortunately, this is all that is required. int _dowildcard = 1; __declspec(noreturn) void help( void ) { _putws( L"\ EOL by Jason Hood .\n\ Version " PVERS L" (" PDATE L"). Public Domain.\n\ http://misc.adoxa.vze.com/\n\ \n\ Rewrite files forcing a particular line ending.\n\ \n\ eol [-r] [d|m|u|z] FILE...\n\ \n\ -r\tRewrite read-only files\n\ d\tDOS CRLF (default)\n\ m\tMac CR\n\ u\tUnix LF\n\ z\tASCIZ NUL\n\ \n\ If the program name ends with:\n\ \ttod or 2dos, assume d;\n\ \ttom or 2mac, assume m;\n\ \ttou or 2unix, assume u;\n\ \ttoz or 2asciz, assume z.\n\ \n\ Current line ending is autodetected: if there are no LFs or CRs in the first\n\ 128KiB, assume ASCIZ; if there are no LFs, assume Mac; otherwise DOS or Unix\n\ (any CRs before LF will be included in the line ending).\ " ); exit( 0 ); } #define BUFFER (128*1024) char buffer[BUFFER]; DWORD buflen; HANDLE hbuf; LPWSTR name; BOOL ro; const char* eol_ptr; int eol_len; BOOL set_format( WCHAR fmt ) { static const char eol[] = "\r\n"; switch (fmt) { default: return FALSE; case 'd': eol_ptr = eol; eol_len = 2; break; case 'm': eol_ptr = eol; eol_len = 1; break; case 'u': eol_ptr = eol+1; eol_len = 1; break; case 'z': eol_ptr = eol+2; eol_len = 1; break; } return TRUE; } BOOL print_error( LPWSTR name, LPWSTR func ) { LPWSTR errmsg; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), 0, (LPWSTR)(LPVOID)&errmsg, 0, NULL ); if (func == NULL) wprintf( L"%s: %s", name, errmsg ); else wprintf( L"%s (%s): %s", name, func, errmsg ); LocalFree( errmsg ); return FALSE; } BOOL flush_buffer( void ) { if (buflen != 0) { DWORD written; WriteFile( hbuf, buffer, buflen, &written, NULL ); if (written != buflen) return print_error( name, L"WriteFile" ); buflen = 0; } return TRUE; } BOOL put_buffer( const char* mem, DWORD size ) { if (buflen + size >= BUFFER) { if (!flush_buffer()) return FALSE; if (size >= BUFFER) { DWORD written; DWORD rem = size % BUFFER; size -= rem; WriteFile( hbuf, mem, size, &written, NULL ); if (written != size) return print_error( name, L"WriteFile" ); mem += size; size = rem; } } memcpy( buffer+buflen, mem, size ); buflen += size; return TRUE; } BOOL convert( const char* mem, DWORD size ) { BOOL rc = TRUE; BOOL changed = FALSE; char eol = '\n'; // Determine the existing ending. Assume Mac if no LF within the first 128Ki. int len = (size > 128*1024) ? 128*1024 : size; const char* nl = memchr( mem, '\n', len ); if (nl == NULL) { nl = memchr( mem, '\r', len ); eol = (nl == NULL) ? '\0' : '\r'; } while (size != 0) { nl = memchr( mem, eol, size ); if (nl == NULL) { rc = put_buffer( mem, size ); break; } len = nl - mem; size -= len + 1; if (eol == '\n') { // Converting to DOS sometimes adds extra CRs, strip 'em all. while (len > 0 && mem[len-1] == '\r') --len; } if (!changed) { if (eol_len == 2) // CRLF { if (nl - mem != len + 1) // didn't find one CR changed = TRUE; } else if (nl - mem != len // removed CRs || *nl != *eol_ptr) { changed = TRUE; } } rc = (put_buffer( mem, len ) && put_buffer( eol_ptr, eol_len )); if (!rc) break; mem = nl + 1; } if (rc && changed) rc = flush_buffer(); else buflen = 0; return (rc && changed); } BOOL EOL( void ) { DWORD attrib; HANDLE file, map; LARGE_INTEGER size; FILETIME ctime, atime, mtime; WCHAR tmp[MAX_PATH]; BOOL rc; attrib = GetFileAttributes( name ); if (attrib == INVALID_FILE_ATTRIBUTES) { return print_error( name, (GetLastError() == ERROR_FILE_NOT_FOUND) ? NULL : L"GetFileAttributes" ); } if (attrib & FILE_ATTRIBUTE_READONLY) { if (!ro) { wprintf( L"%s: Read-only.\n", name ); return FALSE; } if (!SetFileAttributes( name, attrib & ~FILE_ATTRIBUTE_READONLY )) return print_error( name, L"SetFileAttributes" ); } file = CreateFile( name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (file == INVALID_HANDLE_VALUE) return print_error( name, L"CreateFile" ); GetFileSizeEx( file, &size ); if (size.HighPart != 0) { wprintf( L"%s: Unsupported file size.\n", name ); CloseHandle( file ); return FALSE; } if (size.LowPart == 0) { CloseHandle( file ); if (attrib & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributes( name, attrib )) return print_error( name, L"SetFileAttributes" ); } return TRUE; } _snwprintf( tmp, MAX_PATH, L"%s.eol$", name ); hbuf = CreateFile( tmp, GENERIC_WRITE, 0, NULL, CREATE_NEW, attrib, file ); if (hbuf == INVALID_HANDLE_VALUE) { CloseHandle( file ); return print_error( tmp, L"CreateFile" ); } map = CreateFileMapping( file, NULL, PAGE_READONLY, 0, 0, NULL ); if (map != NULL) { LPVOID mem = MapViewOfFile( map, FILE_MAP_READ, 0, 0, size.LowPart ); if (mem != NULL) { rc = convert( mem, size.LowPart ); UnmapViewOfFile( mem ); } else { rc = print_error( name, L"MapViewOfFile" ); } CloseHandle( map ); } else { rc = print_error( name, L"CreateFileMapping" ); } if (rc) { if (!GetFileTime( file, &ctime, &atime, &mtime )) rc = print_error( name, L"GetFileTime" ); else if (!SetFileTime( hbuf, &ctime, &atime, &mtime )) rc = print_error( tmp, L"SetFileTime" ); } CloseHandle( hbuf ); CloseHandle( file ); if (rc && !MoveFileEx( tmp, name, MOVEFILE_REPLACE_EXISTING )) rc = print_error( name, tmp ); if (!rc) { if (attrib & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributes( tmp, 0 )) return print_error( tmp, L"SetFileAttributes" ); } if (!DeleteFile( tmp )) print_error( tmp, L"DeleteFile" ); } return rc; } int wmain( int argc, wchar_t* argv[] ) { LPWSTR s, right; int len; BOOL fixed = FALSE; if (argc == 1 || wcscmp( argv[1], L"/?" ) == 0 || wcscmp( argv[1], L"-?" ) == 0 || wcscmp( argv[1], L"--help" ) == 0) { help(); } if (wcscmp( argv[1], L"--version" ) == 0) { _putws( L"EOL version " PVERS L" (" PDATE L")." ); exit( 0 ); } set_format( 'd' ); // Check what we're called, adjust format accordingly. for (right = NULL, s = name = argv[0]; *s != '\0'; ++s) { if (*s == '\\' || *s == '/') name = s+1; if (*s == '.') right = s; } if (right == NULL || right <= name) right = s; else *right = '\0'; len = right - name; if (len >= 3) { _wcslwr( name ); right -= 3; if (wcsncmp( right, L"to", 2 ) == 0 && set_format( right[2] )) { fixed = TRUE; } else if (len >= 4) { --right; if (wcscmp( right, L"2dos" ) == 0) { fixed = TRUE; } else if (wcscmp( right, L"2mac" ) == 0) { set_format( 'm' ); fixed = TRUE; } } if (!fixed && len >= 5) { --right; if (wcscmp( right, L"2unix" ) == 0) { set_format( 'u' ); fixed = TRUE; } } if (!fixed && len >= 6) { --right; if (wcscmp( right, L"2asciz" ) == 0) { set_format( 'z' ); fixed = TRUE; } } } if (wcscmp( argv[1], L"-r" ) == 0) { ro = TRUE; if ((++argv)[1] == NULL) return 0; } if (!fixed && argv[1][1] == '\0' && set_format( *argv[1] )) ++argv; while (*++argv != NULL) { name = *argv; EOL(); } return 0; }