mirror of
https://github.com/notepad-plus-plus/notepad-plus-plus.git
synced 2025-07-23 05:45:00 +02:00
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()
|
bool Notepad_plus::replaceInFiles()
|
||||||
{
|
{
|
||||||
|
LongRunningOperation op;
|
||||||
|
|
||||||
const TCHAR *dir2Search = _findReplaceDlg.getDir2Search();
|
const TCHAR *dir2Search = _findReplaceDlg.getDir2Search();
|
||||||
if (!dir2Search[0] || !::PathFileExists(dir2Search))
|
if (!dir2Search[0] || !::PathFileExists(dir2Search))
|
||||||
{
|
{
|
||||||
@ -6633,7 +6635,7 @@ DWORD WINAPI Notepad_plus::backupDocument(void * /*param*/)
|
|||||||
if (!isSnapshotMode)
|
if (!isSnapshotMode)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
MainFileManager->backupCurrentBuffer();
|
::PostMessage(Notepad_plus_Window::gNppHWND, NPPM_INTERNAL_SAVEBACKUP, 0, 0);
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -623,7 +623,7 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
|
|||||||
case NPPM_INTERNAL_SAVECURRENTSESSION:
|
case NPPM_INTERNAL_SAVECURRENTSESSION:
|
||||||
{
|
{
|
||||||
NppParameters *nppParam = NppParameters::getInstance();
|
NppParameters *nppParam = NppParameters::getInstance();
|
||||||
const NppGUI nppGui = nppParam->getNppGUI();
|
const NppGUI& nppGui = nppParam->getNppGUI();
|
||||||
|
|
||||||
if (nppGui._rememberLastSession && !nppGui._isCmdlineNosessionActivated)
|
if (nppGui._rememberLastSession && !nppGui._isCmdlineNosessionActivated)
|
||||||
{
|
{
|
||||||
@ -634,6 +634,16 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case NPPM_INTERNAL_SAVEBACKUP:
|
||||||
|
{
|
||||||
|
if (NppParameters::getInstance()->getNppGUI().isSnapshotMode())
|
||||||
|
{
|
||||||
|
MainFileManager->backupCurrentBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
case NPPM_INTERNAL_RELOADNATIVELANG:
|
case NPPM_INTERNAL_RELOADNATIVELANG:
|
||||||
{
|
{
|
||||||
reloadLang();
|
reloadLang();
|
||||||
|
@ -1464,23 +1464,35 @@ void Notepad_plus::command(int id)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case IDM_EDIT_TRIMTRAILING:
|
case IDM_EDIT_TRIMTRAILING:
|
||||||
|
{
|
||||||
|
LongRunningOperation op;
|
||||||
|
|
||||||
_pEditView->execute(SCI_BEGINUNDOACTION);
|
_pEditView->execute(SCI_BEGINUNDOACTION);
|
||||||
doTrim(lineTail);
|
doTrim(lineTail);
|
||||||
_pEditView->execute(SCI_ENDUNDOACTION);
|
_pEditView->execute(SCI_ENDUNDOACTION);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IDM_EDIT_TRIMLINEHEAD:
|
case IDM_EDIT_TRIMLINEHEAD:
|
||||||
|
{
|
||||||
|
LongRunningOperation op;
|
||||||
|
|
||||||
_pEditView->execute(SCI_BEGINUNDOACTION);
|
_pEditView->execute(SCI_BEGINUNDOACTION);
|
||||||
doTrim(lineHeader);
|
doTrim(lineHeader);
|
||||||
_pEditView->execute(SCI_ENDUNDOACTION);
|
_pEditView->execute(SCI_ENDUNDOACTION);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IDM_EDIT_TRIM_BOTH:
|
case IDM_EDIT_TRIM_BOTH:
|
||||||
|
{
|
||||||
|
LongRunningOperation op;
|
||||||
|
|
||||||
_pEditView->execute(SCI_BEGINUNDOACTION);
|
_pEditView->execute(SCI_BEGINUNDOACTION);
|
||||||
doTrim(lineTail);
|
doTrim(lineTail);
|
||||||
doTrim(lineHeader);
|
doTrim(lineHeader);
|
||||||
_pEditView->execute(SCI_ENDUNDOACTION);
|
_pEditView->execute(SCI_ENDUNDOACTION);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IDM_EDIT_EOL2WS:
|
case IDM_EDIT_EOL2WS:
|
||||||
_pEditView->execute(SCI_BEGINUNDOACTION);
|
_pEditView->execute(SCI_BEGINUNDOACTION);
|
||||||
@ -1490,6 +1502,9 @@ void Notepad_plus::command(int id)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case IDM_EDIT_TRIMALL:
|
case IDM_EDIT_TRIMALL:
|
||||||
|
{
|
||||||
|
LongRunningOperation op;
|
||||||
|
|
||||||
_pEditView->execute(SCI_BEGINUNDOACTION);
|
_pEditView->execute(SCI_BEGINUNDOACTION);
|
||||||
doTrim(lineTail);
|
doTrim(lineTail);
|
||||||
doTrim(lineHeader);
|
doTrim(lineHeader);
|
||||||
@ -1497,6 +1512,7 @@ void Notepad_plus::command(int id)
|
|||||||
_pEditView->execute(SCI_LINESJOIN);
|
_pEditView->execute(SCI_LINESJOIN);
|
||||||
_pEditView->execute(SCI_ENDUNDOACTION);
|
_pEditView->execute(SCI_ENDUNDOACTION);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IDM_EDIT_TAB2SW:
|
case IDM_EDIT_TAB2SW:
|
||||||
wsTabConvert(tab2Space);
|
wsTabConvert(tab2Space);
|
||||||
|
@ -68,6 +68,7 @@ namespace // anonymous
|
|||||||
|
|
||||||
return defvalue; // fallback unknown
|
return defvalue; // fallback unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
@ -795,35 +796,6 @@ bool FileManager::backupCurrentBuffer()
|
|||||||
{
|
{
|
||||||
if (buffer->isModified()) // buffer dirty and modified, write the backup file
|
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();
|
UniMode mode = buffer->getUnicodeMode();
|
||||||
if (mode == uniCookie)
|
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
|
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
|
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
|
else // buffer dirty but unmodified
|
||||||
{
|
{
|
||||||
@ -957,47 +922,8 @@ bool FileManager::backupCurrentBuffer()
|
|||||||
return result;
|
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)
|
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);
|
Buffer* buffer = getBufferByID(id);
|
||||||
bool result = true;
|
bool result = true;
|
||||||
generic_string backupFilePath = buffer->getBackupFileName();
|
generic_string backupFilePath = buffer->getBackupFileName();
|
||||||
@ -1008,33 +934,13 @@ bool FileManager::deleteBufferBackup(BufferID id)
|
|||||||
result = (::DeleteFile(backupFilePath.c_str()) != 0);
|
result = (::DeleteFile(backupFilePath.c_str()) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set to signaled state via destructor EventReset.
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, generic_string * error_msg)
|
bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, generic_string * error_msg)
|
||||||
{
|
{
|
||||||
HANDLE writeEvent = ::OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("nppWrittingEvent"));
|
LongRunningOperation op;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
Buffer* buffer = getBufferByID(id);
|
||||||
bool isHiddenOrSys = false;
|
bool isHiddenOrSys = false;
|
||||||
DWORD attrib = 0;
|
DWORD attrib = 0;
|
||||||
@ -1100,7 +1006,7 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g
|
|||||||
if (lengthDoc == 0)
|
if (lengthDoc == 0)
|
||||||
items_written = 1;
|
items_written = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the language du fichier
|
// check the language du fichier
|
||||||
LangType language = detectLanguageFromTextBegining((unsigned char *)buf, lengthDoc);
|
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)
|
if(error_msg != NULL)
|
||||||
*error_msg = TEXT("Failed to save file.\nNot enough space on disk to save file?");
|
*error_msg = TEXT("Failed to save file.\nNot enough space on disk to save file?");
|
||||||
|
|
||||||
// set to signaled state via destructor EventReset.
|
|
||||||
return false;
|
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
|
return true; //all done
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1154,10 +1058,9 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g
|
|||||||
::DeleteFile(backupFilePath.c_str());
|
::DeleteFile(backupFilePath.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// set to signaled state via destructor EventReset.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// set to signaled state via destructor EventReset.
|
|
||||||
return false;
|
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
|
const NewDocDefaultSettings & ndds = (pNppParamInst->getNppGUI()).getNewDocDefaultSettings(); // for ndds._format
|
||||||
eolFormat = 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 (fileSize == 0)
|
||||||
{
|
{
|
||||||
if (ndds._unicodeMode == uniCookie && ndds._openAnsiAsUtf8)
|
if (ndds._unicodeMode == uniCookie && ndds._openAnsiAsUtf8)
|
||||||
|
@ -431,6 +431,7 @@
|
|||||||
#define NPPM_INTERNAL_SETWORDCHARS (NOTEPADPLUS_USER_INTERNAL + 45)
|
#define NPPM_INTERNAL_SETWORDCHARS (NOTEPADPLUS_USER_INTERNAL + 45)
|
||||||
#define NPPM_INTERNAL_EXPORTFUNCLISTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 46)
|
#define NPPM_INTERNAL_EXPORTFUNCLISTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 46)
|
||||||
#define NPPM_INTERNAL_PRNTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 47)
|
#define NPPM_INTERNAL_PRNTANDQUIT (NOTEPADPLUS_USER_INTERNAL + 47)
|
||||||
|
#define NPPM_INTERNAL_SAVEBACKUP (NOTEPADPLUS_USER_INTERNAL + 48)
|
||||||
|
|
||||||
//wParam: 0
|
//wParam: 0
|
||||||
//lParam: document new index
|
//lParam: document new index
|
||||||
|
Loading…
x
Reference in New Issue
Block a user