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
This commit is contained in:
parent
c6e1a95098
commit
abc6bc144b
@ -1479,6 +1479,8 @@ void Notepad_plus::getMatchedFileNames(const TCHAR *dir, const vector<generic_st
|
||||
|
||||
bool Notepad_plus::replaceInFiles()
|
||||
{
|
||||
LongRunningOperation op;
|
||||
|
||||
const TCHAR *dir2Search = _findReplaceDlg.getDir2Search();
|
||||
if (!dir2Search[0] || !::PathFileExists(dir2Search))
|
||||
{
|
||||
@ -6633,7 +6635,7 @@ DWORD WINAPI Notepad_plus::backupDocument(void * /*param*/)
|
||||
if (!isSnapshotMode)
|
||||
break;
|
||||
|
||||
MainFileManager->backupCurrentBuffer();
|
||||
::PostMessage(Notepad_plus_Window::gNppHWND, NPPM_INTERNAL_SAVEBACKUP, 0, 0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user