Migrate timestamps from time_t to FILETIME and store them in UTC universally

Fixes #4491, #3969, #2535 and #215.
This commit is contained in:
Silent 2018-05-31 15:32:44 +02:00 committed by Don HO
parent 08eb29e3d6
commit 9cb8fce854
7 changed files with 35 additions and 28 deletions

View File

@ -4952,7 +4952,7 @@ void Notepad_plus::getCurrentOpenedFiles(Session & session, bool includUntitledD
generic_string languageName = getLangFromMenu(buf); generic_string languageName = getLangFromMenu(buf);
const TCHAR *langName = languageName.c_str(); const TCHAR *langName = languageName.c_str();
sessionFileInfo sfi(buf->getFullPathName(), langName, buf->getEncoding(), buf->getPosition(editView), buf->getBackupFileName().c_str(), int(buf->getLastModifiedTimestamp()), buf->getMapPosition()); sessionFileInfo sfi(buf->getFullPathName(), langName, buf->getEncoding(), buf->getPosition(editView), buf->getBackupFileName().c_str(), buf->getLastModifiedTimestamp(), buf->getMapPosition());
_invisibleEditView.execute(SCI_SETDOCPOINTER, 0, buf->getDocument()); _invisibleEditView.execute(SCI_SETDOCPOINTER, 0, buf->getDocument());
size_t maxLine = static_cast<size_t>(_invisibleEditView.execute(SCI_GETLINECOUNT)); size_t maxLine = static_cast<size_t>(_invisibleEditView.execute(SCI_GETLINECOUNT));

View File

@ -174,7 +174,7 @@ public:
//! \name File Operations //! \name File Operations
//@{ //@{
//The doXXX functions apply to a single buffer and dont need to worry about views, with the excpetion of doClose, since closing one view doesnt have to mean the document is gone //The doXXX functions apply to a single buffer and dont need to worry about views, with the excpetion of doClose, since closing one view doesnt have to mean the document is gone
BufferID doOpen(const generic_string& fileName, bool isRecursive = false, bool isReadOnly = false, int encoding = -1, const TCHAR *backupFileName = NULL, time_t fileNameTimestamp = 0); BufferID doOpen(const generic_string& fileName, bool isRecursive = false, bool isReadOnly = false, int encoding = -1, const TCHAR *backupFileName = NULL, FILETIME fileNameTimestamp = {});
bool doReload(BufferID id, bool alert = true); bool doReload(BufferID id, bool alert = true);
bool doSave(BufferID, const TCHAR * filename, bool isSaveCopy = false); bool doSave(BufferID, const TCHAR * filename, bool isSaveCopy = false);
void doClose(BufferID, int whichOne, bool doDeleteBackup = false); void doClose(BufferID, int whichOne, bool doDeleteBackup = false);

View File

@ -121,7 +121,7 @@ DWORD WINAPI Notepad_plus::monitorFileOnChange(void * params)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
BufferID Notepad_plus::doOpen(const generic_string& fileName, bool isRecursive, bool isReadOnly, int encoding, const TCHAR *backupFileName, time_t fileNameTimestamp) BufferID Notepad_plus::doOpen(const generic_string& fileName, bool isRecursive, bool isReadOnly, int encoding, const TCHAR *backupFileName, FILETIME fileNameTimestamp)
{ {
const rsize_t longFileNameBufferSize = MAX_PATH; // TODO stop using fixed-size buffer const rsize_t longFileNameBufferSize = MAX_PATH; // TODO stop using fixed-size buffer
if (fileName.size() >= longFileNameBufferSize - 1) // issue with all other sub-routines if (fileName.size() >= longFileNameBufferSize - 1) // issue with all other sub-routines

View File

@ -2050,8 +2050,9 @@ bool NppParameters::getSessionFromXmlTree(TiXmlDocument *pSessionDoc, Session *p
const TCHAR *encStr = (childNode->ToElement())->Attribute(TEXT("encoding"), &encoding); const TCHAR *encStr = (childNode->ToElement())->Attribute(TEXT("encoding"), &encoding);
const TCHAR *backupFilePath = (childNode->ToElement())->Attribute(TEXT("backupFilePath")); const TCHAR *backupFilePath = (childNode->ToElement())->Attribute(TEXT("backupFilePath"));
int fileModifiedTimestamp = 0; FILETIME fileModifiedTimestamp;
(childNode->ToElement())->Attribute(TEXT("originalFileLastModifTimestamp"), &fileModifiedTimestamp); (childNode->ToElement())->Attribute(TEXT("originalFileLastModifTimestamp"), reinterpret_cast<int32_t*>(&fileModifiedTimestamp.dwLowDateTime));
(childNode->ToElement())->Attribute(TEXT("originalFileLastModifTimestampHigh"), reinterpret_cast<int32_t*>(&fileModifiedTimestamp.dwHighDateTime));
sessionFileInfo sfi(fileName, langName, encStr?encoding:-1, position, backupFilePath, fileModifiedTimestamp, mapPosition); sessionFileInfo sfi(fileName, langName, encStr?encoding:-1, position, backupFilePath, fileModifiedTimestamp, mapPosition);
@ -2985,7 +2986,8 @@ void NppParameters::writeSession(const Session & session, const TCHAR *fileName)
(fileNameNode->ToElement())->SetAttribute(TEXT("encoding"), viewSessionFiles[i]._encoding); (fileNameNode->ToElement())->SetAttribute(TEXT("encoding"), viewSessionFiles[i]._encoding);
(fileNameNode->ToElement())->SetAttribute(TEXT("filename"), viewSessionFiles[i]._fileName.c_str()); (fileNameNode->ToElement())->SetAttribute(TEXT("filename"), viewSessionFiles[i]._fileName.c_str());
(fileNameNode->ToElement())->SetAttribute(TEXT("backupFilePath"), viewSessionFiles[i]._backupFilePath.c_str()); (fileNameNode->ToElement())->SetAttribute(TEXT("backupFilePath"), viewSessionFiles[i]._backupFilePath.c_str());
(fileNameNode->ToElement())->SetAttribute(TEXT("originalFileLastModifTimestamp"), static_cast<int32_t>(viewSessionFiles[i]._originalFileLastModifTimestamp)); (fileNameNode->ToElement())->SetAttribute(TEXT("originalFileLastModifTimestamp"), static_cast<int32_t>(viewSessionFiles[i]._originalFileLastModifTimestamp.dwLowDateTime));
(fileNameNode->ToElement())->SetAttribute(TEXT("originalFileLastModifTimestampHigh"), static_cast<int32_t>(viewSessionFiles[i]._originalFileLastModifTimestamp.dwHighDateTime));
// docMap // docMap
(fileNameNode->ToElement())->SetAttribute(TEXT("mapFirstVisibleDisplayLine"), viewSessionFiles[i]._mapPos._firstVisibleDisplayLine); (fileNameNode->ToElement())->SetAttribute(TEXT("mapFirstVisibleDisplayLine"), viewSessionFiles[i]._mapPos._firstVisibleDisplayLine);

View File

@ -162,7 +162,7 @@ private:
struct sessionFileInfo : public Position struct sessionFileInfo : public Position
{ {
sessionFileInfo(const TCHAR *fn, const TCHAR *ln, int encoding, Position pos, const TCHAR *backupFilePath, int originalFileLastModifTimestamp, const MapPosition & mapPos) : sessionFileInfo(const TCHAR *fn, const TCHAR *ln, int encoding, Position pos, const TCHAR *backupFilePath, FILETIME originalFileLastModifTimestamp, const MapPosition & mapPos) :
_encoding(encoding), Position(pos), _originalFileLastModifTimestamp(originalFileLastModifTimestamp), _mapPos(mapPos) _encoding(encoding), Position(pos), _originalFileLastModifTimestamp(originalFileLastModifTimestamp), _mapPos(mapPos)
{ {
if (fn) _fileName = fn; if (fn) _fileName = fn;
@ -179,7 +179,7 @@ struct sessionFileInfo : public Position
int _encoding = -1; int _encoding = -1;
generic_string _backupFilePath; generic_string _backupFilePath;
time_t _originalFileLastModifTimestamp = 0; FILETIME _originalFileLastModifTimestamp = {};
MapPosition _mapPos; MapPosition _mapPos;
}; };

View File

@ -144,10 +144,14 @@ void Buffer::setLangType(LangType lang, const TCHAR* userLangName)
void Buffer::updateTimeStamp() void Buffer::updateTimeStamp()
{ {
struct _stat buf; FILETIME timeStamp = {};
time_t timeStamp = (generic_stat(_fullPathName.c_str(), &buf)==0)?buf.st_mtime:0; WIN32_FILE_ATTRIBUTE_DATA attributes;
if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0)
{
timeStamp = attributes.ftLastWriteTime;
}
if (timeStamp != _timeStamp) if (CompareFileTime(&_timeStamp, &timeStamp) != 0)
{ {
_timeStamp = timeStamp; _timeStamp = timeStamp;
doNotify(BufferChangeTimestamp); doNotify(BufferChangeTimestamp);
@ -222,7 +226,7 @@ bool Buffer::checkFileState() //eturns true if the status has been changed (it c
if (_currentStatus == DOC_UNNAMED) //unsaved document cannot change by environment if (_currentStatus == DOC_UNNAMED) //unsaved document cannot change by environment
return false; return false;
struct _stat buf; WIN32_FILE_ATTRIBUTE_DATA attributes;
bool isWow64Off = false; bool isWow64Off = false;
NppParameters *pNppParam = NppParameters::getInstance(); NppParameters *pNppParam = NppParameters::getInstance();
@ -238,18 +242,18 @@ bool Buffer::checkFileState() //eturns true if the status has been changed (it c
_currentStatus = DOC_DELETED; _currentStatus = DOC_DELETED;
_isFileReadOnly = false; _isFileReadOnly = false;
_isDirty = true; //dirty sicne no match with filesystem _isDirty = true; //dirty sicne no match with filesystem
_timeStamp = 0; _timeStamp = {};
doNotify(BufferChangeStatus | BufferChangeReadonly | BufferChangeTimestamp); doNotify(BufferChangeStatus | BufferChangeReadonly | BufferChangeTimestamp);
isOK = true; isOK = true;
} }
else if (_currentStatus == DOC_DELETED && PathFileExists(_fullPathName.c_str())) else if (_currentStatus == DOC_DELETED && PathFileExists(_fullPathName.c_str()))
{ //document has returned from its grave { //document has returned from its grave
if (not generic_stat(_fullPathName.c_str(), &buf)) if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0)
{ {
_isFileReadOnly = (bool)(!(buf.st_mode & _S_IWRITE)); _isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
_currentStatus = DOC_MODIFIED; _currentStatus = DOC_MODIFIED;
_timeStamp = buf.st_mtime; _timeStamp = attributes.ftLastWriteTime;
if (_reloadFromDiskRequestGuard.try_lock()) if (_reloadFromDiskRequestGuard.try_lock())
{ {
@ -259,18 +263,18 @@ bool Buffer::checkFileState() //eturns true if the status has been changed (it c
isOK = true; isOK = true;
} }
} }
else if (not generic_stat(_fullPathName.c_str(), &buf)) else if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0)
{ {
int mask = 0; //status always 'changes', even if from modified to modified int mask = 0; //status always 'changes', even if from modified to modified
bool isFileReadOnly = (bool)(not(buf.st_mode & _S_IWRITE)); bool isFileReadOnly = attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
if (isFileReadOnly != _isFileReadOnly) if (isFileReadOnly != _isFileReadOnly)
{ {
_isFileReadOnly = isFileReadOnly; _isFileReadOnly = isFileReadOnly;
mask |= BufferChangeReadonly; mask |= BufferChangeReadonly;
} }
if (_timeStamp != buf.st_mtime) if (CompareFileTime(&_timeStamp, &attributes.ftLastWriteTime) != 0)
{ {
_timeStamp = buf.st_mtime; _timeStamp = attributes.ftLastWriteTime;
mask |= BufferChangeTimestamp; mask |= BufferChangeTimestamp;
_currentStatus = DOC_MODIFIED; _currentStatus = DOC_MODIFIED;
mask |= BufferChangeStatus; //status always 'changes', even if from modified to modified mask |= BufferChangeStatus; //status always 'changes', even if from modified to modified
@ -300,10 +304,10 @@ bool Buffer::checkFileState() //eturns true if the status has been changed (it c
void Buffer::reload() void Buffer::reload()
{ {
struct _stat buf; WIN32_FILE_ATTRIBUTE_DATA attributes;
if (PathFileExists(_fullPathName.c_str()) && not generic_stat(_fullPathName.c_str(), &buf)) if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0)
{ {
_timeStamp = buf.st_mtime; _timeStamp = attributes.ftLastWriteTime;
_currentStatus = DOC_NEEDRELOAD; _currentStatus = DOC_NEEDRELOAD;
doNotify(BufferChangeTimestamp | BufferChangeStatus); doNotify(BufferChangeTimestamp | BufferChangeStatus);
} }
@ -552,7 +556,7 @@ void FileManager::closeBuffer(BufferID id, ScintillaEditView * identifier)
// backupFileName is sentinel of backup mode: if it's not NULL, then we use it (load it). Otherwise we use filename // backupFileName is sentinel of backup mode: if it's not NULL, then we use it (load it). Otherwise we use filename
BufferID FileManager::loadFile(const TCHAR * filename, Document doc, int encoding, const TCHAR *backupFileName, time_t fileNameTimestamp) BufferID FileManager::loadFile(const TCHAR * filename, Document doc, int encoding, const TCHAR *backupFileName, FILETIME fileNameTimestamp)
{ {
bool ownDoc = false; bool ownDoc = false;
if (doc == NULL) if (doc == NULL)
@ -593,7 +597,8 @@ BufferID FileManager::loadFile(const TCHAR * filename, Document doc, int encodin
newBuf->_currentStatus = DOC_UNNAMED; newBuf->_currentStatus = DOC_UNNAMED;
} }
if (fileNameTimestamp != 0) const FILETIME zeroTime = {};
if (CompareFileTime(&fileNameTimestamp, &zeroTime) != 0)
newBuf->_timeStamp = fileNameTimestamp; newBuf->_timeStamp = fileNameTimestamp;
_buffers.push_back(newBuf); _buffers.push_back(newBuf);

View File

@ -87,7 +87,7 @@ public:
void addBufferReference(BufferID id, ScintillaEditView * identifer); //called by Scintilla etc indirectly void addBufferReference(BufferID id, ScintillaEditView * identifer); //called by Scintilla etc indirectly
BufferID loadFile(const TCHAR * filename, Document doc = NULL, int encoding = -1, const TCHAR *backupFileName = NULL, time_t fileNameTimestamp = 0); //ID == BUFFER_INVALID on failure. If Doc == NULL, a new file is created, otherwise data is loaded in given document BufferID loadFile(const TCHAR * filename, Document doc = NULL, int encoding = -1, const TCHAR *backupFileName = NULL, FILETIME fileNameTimestamp = {}); //ID == BUFFER_INVALID on failure. If Doc == NULL, a new file is created, otherwise data is loaded in given document
BufferID newEmptyDocument(); BufferID newEmptyDocument();
//create Buffer from existing Scintilla, used from new Scintillas. If dontIncrease = true, then the new document number isnt increased afterwards. //create Buffer from existing Scintilla, used from new Scintillas. If dontIncrease = true, then the new document number isnt increased afterwards.
//usefull for temporary but neccesary docs //usefull for temporary but neccesary docs
@ -321,7 +321,7 @@ public:
void setModifiedStatus(bool isModified) { _isModified = isModified; } void setModifiedStatus(bool isModified) { _isModified = isModified; }
generic_string getBackupFileName() const { return _backupFileName; } generic_string getBackupFileName() const { return _backupFileName; }
void setBackupFileName(generic_string fileName) { _backupFileName = fileName; } void setBackupFileName(generic_string fileName) { _backupFileName = fileName; }
time_t getLastModifiedTimestamp() const { return _timeStamp; } FILETIME getLastModifiedTimestamp() const { return _timeStamp; }
bool isLoadedDirty() const bool isLoadedDirty() const
{ {
@ -394,7 +394,7 @@ private:
//Environment properties //Environment properties
DocFileStatus _currentStatus; DocFileStatus _currentStatus;
time_t _timeStamp = 0; // 0 if it's a new doc FILETIME _timeStamp = {}; // 0 if it's a new doc
bool _isFileReadOnly = false; bool _isFileReadOnly = false;
generic_string _fullPathName; generic_string _fullPathName;