From abc6bc144b5e33ca1c3cbe5718f17242475b91f2 Mon Sep 17 00:00:00 2001 From: Pavel Nedev Date: Thu, 2 Aug 2018 17:09:38 +0300 Subject: [PATCH] Fix possible file corruption during backup or power loss or other abnormal N++ termination Fixes #240, Fixes #2381, Fixes #2883, Fixes #4346, Fixes #4655 and probably more issues related to loss of data. Close #4803 --- PowerEditor/src/Notepad_plus.cpp | 4 +- PowerEditor/src/NppBigSwitch.cpp | 12 ++- PowerEditor/src/NppCommands.cpp | 16 +++ PowerEditor/src/ScitillaComponent/Buffer.cpp | 107 +------------------ PowerEditor/src/resource.h | 1 + 5 files changed, 36 insertions(+), 104 deletions(-) diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index f8161136..9b3581e9 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -1479,6 +1479,8 @@ void Notepad_plus::getMatchedFileNames(const TCHAR *dir, const vectorbackupCurrentBuffer(); + ::PostMessage(Notepad_plus_Window::gNppHWND, NPPM_INTERNAL_SAVEBACKUP, 0, 0); } return TRUE; } diff --git a/PowerEditor/src/NppBigSwitch.cpp b/PowerEditor/src/NppBigSwitch.cpp index c9adcb21..9b949672 100644 --- a/PowerEditor/src/NppBigSwitch.cpp +++ b/PowerEditor/src/NppBigSwitch.cpp @@ -623,7 +623,7 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa case NPPM_INTERNAL_SAVECURRENTSESSION: { NppParameters *nppParam = NppParameters::getInstance(); - const NppGUI nppGui = nppParam->getNppGUI(); + const NppGUI& nppGui = nppParam->getNppGUI(); if (nppGui._rememberLastSession && !nppGui._isCmdlineNosessionActivated) { @@ -634,6 +634,16 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa return TRUE; } + case NPPM_INTERNAL_SAVEBACKUP: + { + if (NppParameters::getInstance()->getNppGUI().isSnapshotMode()) + { + MainFileManager->backupCurrentBuffer(); + } + + return TRUE; + } + case NPPM_INTERNAL_RELOADNATIVELANG: { reloadLang(); diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index 60581add..c91d94ba 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -1464,23 +1464,35 @@ void Notepad_plus::command(int id) break; case IDM_EDIT_TRIMTRAILING: + { + LongRunningOperation op; + _pEditView->execute(SCI_BEGINUNDOACTION); doTrim(lineTail); _pEditView->execute(SCI_ENDUNDOACTION); break; + } case IDM_EDIT_TRIMLINEHEAD: + { + LongRunningOperation op; + _pEditView->execute(SCI_BEGINUNDOACTION); doTrim(lineHeader); _pEditView->execute(SCI_ENDUNDOACTION); break; + } case IDM_EDIT_TRIM_BOTH: + { + LongRunningOperation op; + _pEditView->execute(SCI_BEGINUNDOACTION); doTrim(lineTail); doTrim(lineHeader); _pEditView->execute(SCI_ENDUNDOACTION); break; + } case IDM_EDIT_EOL2WS: _pEditView->execute(SCI_BEGINUNDOACTION); @@ -1490,6 +1502,9 @@ void Notepad_plus::command(int id) break; case IDM_EDIT_TRIMALL: + { + LongRunningOperation op; + _pEditView->execute(SCI_BEGINUNDOACTION); doTrim(lineTail); doTrim(lineHeader); @@ -1497,6 +1512,7 @@ void Notepad_plus::command(int id) _pEditView->execute(SCI_LINESJOIN); _pEditView->execute(SCI_ENDUNDOACTION); break; + } case IDM_EDIT_TAB2SW: wsTabConvert(tab2Space); diff --git a/PowerEditor/src/ScitillaComponent/Buffer.cpp b/PowerEditor/src/ScitillaComponent/Buffer.cpp index 60fa0d4f..79892d3d 100644 --- a/PowerEditor/src/ScitillaComponent/Buffer.cpp +++ b/PowerEditor/src/ScitillaComponent/Buffer.cpp @@ -68,6 +68,7 @@ namespace // anonymous return defvalue; // fallback unknown } + } // anonymous namespace @@ -795,35 +796,6 @@ bool FileManager::backupCurrentBuffer() { if (buffer->isModified()) // buffer dirty and modified, write the backup file { - // Synchronization - // This method is called from 2 differents place, so synchronization is important - HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent")); - if (not writeEvent) - { - // no thread yet, create a event with non-signaled, to block all threads - writeEvent = ::CreateEvent(NULL, TRUE, FALSE, TEXT("nppWrittingEvent")); - if (not writeEvent) - { - printStr(TEXT("CreateEvent problem in backupCurrentBuffer()!")); - return false; - } - } - else - { - if (::WaitForSingleObject(writeEvent, INFINITE) != WAIT_OBJECT_0) - { - printStr(TEXT("WaitForSingleObject problem in backupCurrentBuffer()!")); - return false; - } - - // unlocled here, set to non-signaled state, to block all threads - if (not ::ResetEvent(writeEvent)) - { - printStr(TEXT("ResetEvent problem in backupCurrentBuffer()!")); - return false; - } - } - UniMode mode = buffer->getUnicodeMode(); if (mode == uniCookie) mode = uni8Bit; //set the mode to ANSI to prevent converter from adding BOM and performing conversions, Scintilla's data can be copied directly @@ -918,13 +890,6 @@ bool FileManager::backupCurrentBuffer() result = true; //all done } } - // set to signaled state - if (::SetEvent(writeEvent) == NULL) - { - printStr(TEXT("oups!")); - } - // printStr(TEXT("Event released!")); - ::CloseHandle(writeEvent); } else // buffer dirty but unmodified { @@ -957,47 +922,8 @@ bool FileManager::backupCurrentBuffer() return result; } -class EventReset final -{ -public: - explicit EventReset(HANDLE h) - { - _h = h; - } - - ~EventReset() - { - ::SetEvent(_h); - ::CloseHandle(_h); - } - -private: - HANDLE _h; -}; - bool FileManager::deleteBufferBackup(BufferID id) { - HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent")); - if (!writeEvent) - { - // no thread yet, create a event with non-signaled, to block all threads - writeEvent = ::CreateEvent(NULL, TRUE, FALSE, TEXT("nppWrittingEvent")); - } - else - { - if (::WaitForSingleObject(writeEvent, INFINITE) != WAIT_OBJECT_0) - { - // problem!!! - printStr(TEXT("WaitForSingleObject problem in deleteBufferBackup()!")); - return false; - } - - // unlocled here, set to non-signaled state, to block all threads - ::ResetEvent(writeEvent); - } - - EventReset reset(writeEvent); // Will reset event in destructor. - Buffer* buffer = getBufferByID(id); bool result = true; generic_string backupFilePath = buffer->getBackupFileName(); @@ -1008,33 +934,13 @@ bool FileManager::deleteBufferBackup(BufferID id) result = (::DeleteFile(backupFilePath.c_str()) != 0); } - // set to signaled state via destructor EventReset. return result; } - bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, generic_string * error_msg) { - HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent")); - if (!writeEvent) - { - // no thread yet, create a event with non-signaled, to block all threads - writeEvent = ::CreateEvent(NULL, TRUE, FALSE, TEXT("nppWrittingEvent")); - } - else - { //printStr(TEXT("Locked. I wait.")); - if (::WaitForSingleObject(writeEvent, INFINITE) != WAIT_OBJECT_0) - { - // problem!!! - printStr(TEXT("WaitForSingleObject problem in saveBuffer()!")); - return false; - } + LongRunningOperation op; - // unlocled here, set to non-signaled state, to block all threads - ::ResetEvent(writeEvent); - } - - EventReset reset(writeEvent); // Will reset event in destructor. Buffer* buffer = getBufferByID(id); bool isHiddenOrSys = false; DWORD attrib = 0; @@ -1100,7 +1006,7 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g if (lengthDoc == 0) items_written = 1; } - + // check the language du fichier LangType language = detectLanguageFromTextBegining((unsigned char *)buf, lengthDoc); @@ -1113,7 +1019,6 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g if(error_msg != NULL) *error_msg = TEXT("Failed to save file.\nNot enough space on disk to save file?"); - // set to signaled state via destructor EventReset. return false; } @@ -1135,7 +1040,6 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g } */ - // set to signaled state via destructor EventReset. return true; //all done } @@ -1154,10 +1058,9 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g ::DeleteFile(backupFilePath.c_str()); } - // set to signaled state via destructor EventReset. return true; } - // set to signaled state via destructor EventReset. + return false; } @@ -1499,7 +1402,7 @@ bool FileManager::loadFileData(Document doc, const TCHAR * filename, char* data, const NewDocDefaultSettings & ndds = (pNppParamInst->getNppGUI()).getNewDocDefaultSettings(); // for ndds._format eolFormat = ndds._format; - //for empty files, if the default for new files is UTF8, and "Apply to opened ANSI files" is set, apply it + //for empty files, if the default for new files is UTF8, and "Apply to opened ANSI files" is set, apply it if (fileSize == 0) { if (ndds._unicodeMode == uniCookie && ndds._openAnsiAsUtf8) diff --git a/PowerEditor/src/resource.h b/PowerEditor/src/resource.h index 6052070e..69c14d0d 100644 --- a/PowerEditor/src/resource.h +++ b/PowerEditor/src/resource.h @@ -431,6 +431,7 @@ #define NPPM_INTERNAL_SETWORDCHARS (NOTEPADPLUS_USER_INTERNAL + 45) #define NPPM_INTERNAL_EXPORTFUNCLISTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 46) #define NPPM_INTERNAL_PRNTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 47) + #define NPPM_INTERNAL_SAVEBACKUP (NOTEPADPLUS_USER_INTERNAL + 48) //wParam: 0 //lParam: document new index