View Issue Details

IDProjectCategoryView StatusLast Update
0002872WishesUser Menupublic2015-07-08 21:32
ReporterMaximus Assigned ToDrKnS  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version3.0 
Fixed in Version3.0 
Summary0002872: Не ломать кодовую страницу файла FarMenu.ini
DescriptionМне удобно держать FarMenu.ini в UTF-8. Например, удобно изменения в гите смотреть.
Фар такой файл нормально читает, но при любых изменениях через интерфейс - пересохраняет в CP_UNICODE. Приходится каждый раз его открывать в редакторе (с проблемами кодировки) и пересохранять в UTF-8.
TagsNo tags attached.
Build4351

Activities

Maximus

2014-12-01 09:03

reporter  

0001-Implement-CachedWrite-WriteStr-considering-requested.patch (2,540 bytes)   
From 652083cbb2575163edb15618d92fed587754c1e6 Mon Sep 17 00:00:00 2001
From: Maximus5 <ConEmu.Maximus5@gmail.com>
Date: Fri, 28 Nov 2014 20:03:44 +0300
Subject: [PATCH 1/2] Implement CachedWrite::WriteStr considering requested
 codepage

---
 unicode_far/cache.cpp | 37 +++++++++++++++++++++++++++++++++++--
 unicode_far/cache.hpp |  4 +++-
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/unicode_far/cache.cpp b/unicode_far/cache.cpp
index 6645a29..fa3ed2e 100644
--- a/unicode_far/cache.cpp
+++ b/unicode_far/cache.cpp
@@ -188,11 +188,12 @@ bool CachedRead::FillBuffer()
 	return Result;
 }
 
-CachedWrite::CachedWrite(api::File& file):
+CachedWrite::CachedWrite(api::File& file, uintptr_t codepage):
 	file(file),
 	Buffer(0x10000),
 	FreeSize(Buffer.size()),
-	Flushed(false)
+	Flushed(false),
+	codepage(codepage)
 {
 }
 
@@ -233,6 +234,38 @@ bool CachedWrite::Write(LPCVOID Data, size_t DataSize)
 	return Result;
 }
 
+bool CachedWrite::WriteStr(const wchar_t* SaveStr, size_t Length)
+{
+	if (!Length)
+		return true;
+
+	if (codepage == CP_UNICODE)
+	{
+		return Write(SaveStr, Length*sizeof(wchar_t));
+	}
+	else
+	{
+		auto SwapBytes = [](const wchar_t* src, char* dst, size_t count)
+		{
+			return _swab(reinterpret_cast<char*>(const_cast<wchar_t*>(src)), dst, static_cast<int>(count));
+		};
+
+		DWORD length = (codepage == CP_REVERSEBOM?static_cast<DWORD>(Length*sizeof(wchar_t)):WideCharToMultiByte(codepage, 0, SaveStr, Length, nullptr, 0, nullptr, nullptr));
+		char_ptr SaveStrCopy(length);
+
+		if (SaveStrCopy)
+		{
+			if (codepage == CP_REVERSEBOM)
+				SwapBytes(SaveStr, SaveStrCopy.get(), length);
+			else
+				WideCharToMultiByte(codepage, 0, SaveStr, Length, SaveStrCopy.get(), length, nullptr, nullptr);
+
+			return Write(SaveStrCopy.get(),length);
+		}
+	}
+	return false;
+}
+
 bool CachedWrite::Flush()
 {
 	if (!Flushed)
diff --git a/unicode_far/cache.hpp b/unicode_far/cache.hpp
index 75fa6ad..c0fadba 100644
--- a/unicode_far/cache.hpp
+++ b/unicode_far/cache.hpp
@@ -57,9 +57,10 @@ private:
 class CachedWrite: NonCopyable
 {
 public:
-	CachedWrite(api::File& file);
+	CachedWrite(api::File& file, uintptr_t codepage = CP_UNICODE);
 	~CachedWrite();
 	bool Write(LPCVOID Data, size_t DataSize);
+	bool WriteStr(const wchar_t* SaveStr, size_t Length);
 	bool Flush();
 
 private:
@@ -67,4 +68,5 @@ private:
 	std::vector<BYTE> Buffer;
 	size_t FreeSize;
 	bool Flushed;
+	uintptr_t codepage;
 };
-- 
1.9.4.msysgit.2

Maximus

2014-12-01 09:03

reporter  

0002-Don-t-break-FarMenu.ini-s-codepage-saved-by-user.patch (5,549 bytes)   
From ef85be261321d03c1214f78570696ba2339aede4 Mon Sep 17 00:00:00 2001
From: Maximus5 <ConEmu.Maximus5@gmail.com>
Date: Fri, 28 Nov 2014 20:04:01 +0300
Subject: [PATCH 2/2] Don't break FarMenu.ini's codepage saved by user

---
 unicode_far/usermenu.cpp | 66 +++++++++++++++++++++++++++++++++++-------------
 unicode_far/usermenu.hpp |  1 +
 2 files changed, 50 insertions(+), 17 deletions(-)

diff --git a/unicode_far/usermenu.cpp b/unicode_far/usermenu.cpp
index 45e5d1d..b150869 100644
--- a/unicode_far/usermenu.cpp
+++ b/unicode_far/usermenu.cpp
@@ -129,24 +129,24 @@ static void MenuListToFile(const UserMenu::menu_container& Menu, CachedWrite& CW
 {
 	std::for_each(CONST_RANGE(Menu, i)
 	{
-		CW.Write(i.strHotKey.data(), static_cast<DWORD>(i.strHotKey.size()*sizeof(WCHAR)));
-		CW.Write(L":  ", 3*sizeof(WCHAR));
-		CW.Write(i.strLabel.data(), static_cast<DWORD>(i.strLabel.size()*sizeof(WCHAR)));
-		CW.Write(L"\r\n", 2*sizeof(WCHAR));
+		CW.WriteStr(i.strHotKey.data(), static_cast<DWORD>(i.strHotKey.size()));
+		CW.WriteStr(L":  ", 3);
+		CW.WriteStr(i.strLabel.data(), static_cast<DWORD>(i.strLabel.size()));
+		CW.WriteStr(L"\r\n", 2);
 
 		if (i.Submenu)
 		{
-			CW.Write(L"{\r\n", 3*sizeof(WCHAR));
+			CW.WriteStr(L"{\r\n", 3);
 			MenuListToFile(i.Menu, CW);
-			CW.Write(L"}\r\n", 3*sizeof(WCHAR));
+			CW.WriteStr(L"}\r\n", 3);
 		}
 		else
 		{
 			std::for_each(CONST_RANGE(i.Commands, str)
 			{
-				CW.Write(L"    ", 4*sizeof(WCHAR));
-				CW.Write(str.data(), static_cast<DWORD>(str.size()*sizeof(WCHAR)));
-				CW.Write(L"\r\n", 2*sizeof(WCHAR));
+				CW.WriteStr(L"    ", 4);
+				CW.WriteStr(str.data(), static_cast<DWORD>(str.size()));
+				CW.WriteStr(L"\r\n", 2);
 			});
 		}
 	});
@@ -216,7 +216,8 @@ static void MenuFileToList(UserMenu::menu_container& Menu, GetFileString& GetStr
 UserMenu::UserMenu(bool MenuType):
 	m_MenuMode(MM_LOCAL),
 	m_MenuModified(false),
-	m_ItemChanged(false)
+	m_ItemChanged(false),
+	m_MenuCP(CP_UNICODE)
 {
 	ProcessUserMenu(MenuType, string());
 }
@@ -224,7 +225,8 @@ UserMenu::UserMenu(bool MenuType):
 UserMenu::UserMenu(const string& MenuFileName):
 	m_MenuMode(MM_LOCAL),
 	m_MenuModified(false),
-	m_ItemChanged(false)
+	m_ItemChanged(false),
+	m_MenuCP(CP_UNICODE)
 {
 	ProcessUserMenu(false, MenuFileName);
 }
@@ -238,6 +240,8 @@ void UserMenu::SaveMenu(const string& MenuFileName)
 	if (m_MenuModified)
 	{
 		DWORD FileAttr=api::GetFileAttributes(MenuFileName);
+		uintptr_t MenuCP = CP_UNICODE;
+		api::File MenuFile;
 
 		if (FileAttr != INVALID_FILE_ATTRIBUTES)
 		{
@@ -252,15 +256,41 @@ void UserMenu::SaveMenu(const string& MenuFileName)
 
 			if (FileAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))
 				api::SetFileAttributes(MenuFileName,FILE_ATTRIBUTE_NORMAL);
+
+			if (MenuFile.Open(MenuFileName,GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING))
+			{
+				if (!GetFileFormat(MenuFile, MenuCP))
+					MenuCP = CP_UNICODE;
+				else
+					m_MenuCP = MenuCP;
+				MenuFile.Close();
+			}
 		}
 
 		// Don't use CreationDisposition=CREATE_ALWAYS here - it kills alternate streams
-		api::File MenuFile;
 		if (MenuFile.Open(MenuFileName,GENERIC_WRITE, FILE_SHARE_READ, nullptr, FileAttr==INVALID_FILE_ATTRIBUTES?CREATE_NEW:TRUNCATE_EXISTING))
 		{
-			CachedWrite CW(MenuFile);
-			WCHAR Data = SIGN_UNICODE;
-			CW.Write(&Data, 1*sizeof(WCHAR));
+			DWORD dwSignature = 0;
+			size_t SignLength = 0, SignWritten;
+			switch (MenuCP)
+			{
+				case CP_UNICODE:
+					dwSignature = SIGN_UNICODE;
+					SignLength=2;
+					break;
+				case CP_REVERSEBOM:
+					dwSignature = SIGN_REVERSEBOM;
+					SignLength=2;
+					break;
+				case CP_UTF8:
+					dwSignature = SIGN_UTF8;
+					SignLength=3;
+					break;
+			}
+			if (SignLength)
+				MenuFile.Write(&dwSignature,SignLength,SignWritten,nullptr);
+
+			CachedWrite CW(MenuFile, MenuCP);
 			MenuListToFile(m_Menu, CW);
 			CW.Flush();
 			UINT64 Size = 0;
@@ -268,7 +298,7 @@ void UserMenu::SaveMenu(const string& MenuFileName)
 			MenuFile.Close();
 
 			// ���� ���� FarMenu.ini ����, �� ������ ���
-			if (Size<3) // 2 for BOM
+			if (Size<=SignLength)
 			{
 				api::DeleteFile(MenuFileName);
 			}
@@ -357,6 +387,8 @@ void UserMenu::ProcessUserMenu(bool MenuType, const string& MenuFileName)
 			uintptr_t MenuCP;
 			if (!GetFileFormat(MenuFile, MenuCP))
 				MenuCP = CP_OEMCP;
+			else
+				m_MenuCP = MenuCP;
 
 			GetFileString GetStr(MenuFile, MenuCP);
 			MenuFileToList(m_Menu, GetStr);
@@ -648,7 +680,7 @@ int UserMenu::ProcessSingleMenu(std::list<UserMenuItem>& Menu, int MenuPos, std:
 					{
 						auto OldTitle = std::make_unique<ConsoleTitle>();
 						SaveMenu(MenuFileName);
-						auto ShellEditor = FileEditor::create(MenuFileName, CP_UNICODE, FFILEEDIT_DISABLEHISTORY, -1, -1, nullptr);
+						auto ShellEditor = FileEditor::create(MenuFileName, m_MenuCP, FFILEEDIT_DISABLEHISTORY, -1, -1, nullptr);
 						OldTitle.reset();
 						Global->WindowManager->ExecuteModal(ShellEditor);
 						if (!ShellEditor->IsFileChanged() || (!MenuFile.Open(MenuFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING)))
diff --git a/unicode_far/usermenu.hpp b/unicode_far/usermenu.hpp
index 1034e2e..251aeb6 100644
--- a/unicode_far/usermenu.hpp
+++ b/unicode_far/usermenu.hpp
@@ -57,5 +57,6 @@ private:
 	MENUMODE m_MenuMode;
 	bool m_MenuModified;
 	bool m_ItemChanged;
+	uintptr_t m_MenuCP;
 	menu_container m_Menu;
 };
-- 
1.9.4.msysgit.2

2useven10

2014-12-01 11:20

developer   bugnote:0013292

может быть просто всегда сохранять в UTF-8 ?

Maximus

2014-12-01 11:24

reporter   bugnote:0013293

Я за. А кому-то понадобится KOI8-R. Не?

vskirdin

2014-12-01 12:58

administrator   bugnote:0013294

<off>
KOI-8 понадобится M$ :-)
У них до сих пор в клиенте Outlook эта кодировка дефолтовая.
</off>

NightRoman

2015-04-15 16:03

reporter   bugnote:0013635

Голосую за (хоть за сохранение кодировки, хоть за UTF-8 всегда). Столкнулся, как только включил некоторые свои файлы меню в гит.

DrKnS

2015-04-15 21:18

administrator   bugnote:0013636

4351.
Новое меню сохраняется в UTF-8.
Сохранённое в каком-нибудь более другом юникоде таким и останется.
Сохранённое не в юникоде при любом изменении пересохранится в UTF-8.

Issue History

Date Modified Username Field Change
2014-12-01 08:58 Maximus New Issue
2014-12-01 09:03 Maximus File Added: 0001-Implement-CachedWrite-WriteStr-considering-requested.patch
2014-12-01 09:03 Maximus File Added: 0002-Don-t-break-FarMenu.ini-s-codepage-saved-by-user.patch
2014-12-01 11:20 2useven10 Note Added: 0013292
2014-12-01 11:24 Maximus Note Added: 0013293
2014-12-01 11:51 DrKnS Assigned To => DrKnS
2014-12-01 11:51 DrKnS Status new => assigned
2014-12-01 12:58 vskirdin Note Added: 0013294
2015-04-15 16:03 NightRoman Note Added: 0013635
2015-04-15 21:18 DrKnS Note Added: 0013636
2015-04-15 21:18 DrKnS Status assigned => feedback
2015-07-08 21:32 DrKnS Build => 4351
2015-07-08 21:32 DrKnS Status feedback => closed
2015-07-08 21:32 DrKnS Resolution open => fixed
2015-07-08 21:32 DrKnS Fixed in Version => 3.0