inline bool FileManager::loadFileData(Document doc, const TCHAR * filename, char* data, Utf8_16_Read * UnicodeConvertor, LangType & language, int & encoding, FormatType* pFormat) { FILE *fp = generic_fopen(filename, TEXT("rb")); if (!fp) return false; //Get file size _fseeki64 (fp , 0 , SEEK_END); unsigned __int64 fileSize =_ftelli64(fp); rewind(fp); // size/6 is the normal room Scintilla keeps for editing, but here we limit it to 1MiB when loading (maybe we want to load big files without editing them too much) unsigned __int64 bufferSizeRequested = fileSize + min(1<<20,fileSize/6); // As a 32bit application, we cannot allocate 2 buffer of more than INT_MAX size (it takes the whole address space) if(bufferSizeRequested > INT_MAX) { ::MessageBox(NULL, TEXT("File is too big to be opened by Notepad++"), TEXT("File open problem"), MB_OK|MB_APPLMODAL); /* _nativeLangSpeaker.messageBox("NbFileToOpenImportantWarning", _pPublicInterface->getHSelf(), TEXT("File is too big to be opened by Notepad++"), TEXT("File open problem"), MB_OK|MB_APPLMODAL); */ fclose(fp); return false; } //Setup scratchtilla for new filedata _pscratchTilla->execute(SCI_SETSTATUS, SC_STATUS_OK); // reset error status _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, doc); bool ro = _pscratchTilla->execute(SCI_GETREADONLY) != 0; if (ro) { _pscratchTilla->execute(SCI_SETREADONLY, false); } _pscratchTilla->execute(SCI_CLEARALL); if (language < L_EXTERNAL) { _pscratchTilla->execute(SCI_SETLEXER, ScintillaEditView::langNames[language].lexerID); } else { int id = language - L_EXTERNAL; TCHAR * name = NppParameters::getInstance()->getELCFromIndex(id)._name; WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance(); const char *pName = wmc->wchar2char(name, CP_ACP); _pscratchTilla->execute(SCI_SETLEXERLANGUAGE, 0, (LPARAM)pName); } if (encoding != -1) _pscratchTilla->execute(SCI_SETCODEPAGE, SC_CP_UTF8); bool success = true; FormatType format = FormatType::unknown; __try { // First allocate enough memory for the whole file (this will reduce memory copy during loading) _pscratchTilla->execute(SCI_ALLOCATE, WPARAM(bufferSizeRequested)); if (_pscratchTilla->execute(SCI_GETSTATUS) != SC_STATUS_OK) throw; size_t lenFile = 0; size_t lenConvert = 0; //just in case conversion results in 0, but file not empty bool isFirstTime = true; int incompleteMultibyteChar = 0; do { lenFile = fread(data+incompleteMultibyteChar, 1, blockSize-incompleteMultibyteChar, fp) + incompleteMultibyteChar; if (lenFile == 0) break; if (isFirstTime) { // check if file contain any BOM if (Utf8_16_Read::determineEncoding((unsigned char *)data, lenFile) != uni8Bit) { // if file contains any BOM, then encoding will be erased, // and the document will be interpreted as UTF encoding = -1; } else if (encoding == -1) { if (NppParameters::getInstance()->getNppGUI()._detectEncoding) encoding = detectCodepage(data, lenFile); } if (language == L_TEXT) { // check the language du fichier language = detectLanguageFromTextBegining((unsigned char *)data, lenFile); } isFirstTime = false; } if (encoding != -1) { if (encoding == SC_CP_UTF8) { // Pass through UTF-8 (this does not check validity of characters, thus inserting a multi-byte character in two halfs is working) _pscratchTilla->execute(SCI_APPENDTEXT, lenFile, (LPARAM)data); } else { WcharMbcsConvertor* wmc = WcharMbcsConvertor::getInstance(); int newDataLen = 0; const char *newData = wmc->encode(encoding, SC_CP_UTF8, data, lenFile, &newDataLen, &incompleteMultibyteChar); _pscratchTilla->execute(SCI_APPENDTEXT, newDataLen, (LPARAM)newData); } if (format == FormatType::unknown) format = getEOLFormatForm(data, lenFile, FormatType::unknown); } else { lenConvert = UnicodeConvertor->convert(data, lenFile); _pscratchTilla->execute(SCI_APPENDTEXT, lenConvert, (LPARAM)(UnicodeConvertor->getNewBuf())); } if (_pscratchTilla->execute(SCI_GETSTATUS) != SC_STATUS_OK) throw; if (incompleteMultibyteChar != 0) { // copy bytes to next buffer memcpy(data, data+blockSize-incompleteMultibyteChar, incompleteMultibyteChar); } } while (lenFile > 0); } __except(EXCEPTION_EXECUTE_HANDLER) //TODO: should filter correctly for other exceptions; the old filter(GetExceptionCode(), GetExceptionInformation()) was only catching access violations { ::MessageBox(NULL, TEXT("File is too big to be opened by Notepad++"), TEXT("File open problem"), MB_OK|MB_APPLMODAL); success = false; } fclose(fp); // broadcast the format if (pFormat != nullptr) *pFormat = (format != FormatType::unknown) ? format : FormatType::osdefault; _pscratchTilla->execute(SCI_EMPTYUNDOBUFFER); _pscratchTilla->execute(SCI_SETSAVEPOINT); if (ro) _pscratchTilla->execute(SCI_SETREADONLY, true); _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, _scratchDocDefault); return success; }
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; } // 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 isHidden = false; bool isSys = false; DWORD attrib = 0; TCHAR fullpath[MAX_PATH]; ::GetFullPathName(filename, MAX_PATH, fullpath, NULL); ::GetLongPathName(fullpath, fullpath, MAX_PATH); if (PathFileExists(fullpath)) { attrib = ::GetFileAttributes(fullpath); if (attrib != INVALID_FILE_ATTRIBUTES) { isHidden = (attrib & FILE_ATTRIBUTE_HIDDEN) != 0; if (isHidden) ::SetFileAttributes(filename, attrib & ~FILE_ATTRIBUTE_HIDDEN); isSys = (attrib & FILE_ATTRIBUTE_SYSTEM) != 0; if (isSys) ::SetFileAttributes(filename, attrib & ~FILE_ATTRIBUTE_SYSTEM); } } 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 Utf8_16_Write UnicodeConvertor; UnicodeConvertor.setEncoding(mode); int encoding = buffer->getEncoding(); FILE *fp = UnicodeConvertor.fopen(fullpath, TEXT("wb")); if (fp) { _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, buffer->_doc); //generate new document int lengthDoc = _pscratchTilla->getCurrentDocLen(); char* buf = (char*)_pscratchTilla->execute(SCI_GETCHARACTERPOINTER); //to get characters directly from Scintilla buffer size_t items_written = 0; if (encoding == -1) //no special encoding; can be handled directly by Utf8_16_Write { items_written = UnicodeConvertor.fwrite(buf, lengthDoc); if (lengthDoc == 0) items_written = 1; } else { WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance(); int grabSize; for (int i = 0; i < lengthDoc; i += grabSize) { grabSize = lengthDoc - i; if (grabSize > blockSize) grabSize = blockSize; int newDataLen = 0; int incompleteMultibyteChar = 0; const char *newData = wmc->encode(SC_CP_UTF8, encoding, buf+i, grabSize, &newDataLen, &incompleteMultibyteChar); grabSize -= incompleteMultibyteChar; items_written = UnicodeConvertor.fwrite(newData, newDataLen); } if (lengthDoc == 0) items_written = 1; } // check the language du fichier LangType language = detectLanguageFromTextBegining((unsigned char *)buf, lengthDoc); UnicodeConvertor.fclose(); // Error, we didn't write the entire document to disk. // Note that fwrite() doesn't return the number of bytes written, but rather the number of ITEMS. if(items_written != 1) { 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; } if (isHidden) ::SetFileAttributes(fullpath, attrib | FILE_ATTRIBUTE_HIDDEN); if (isSys) ::SetFileAttributes(fullpath, attrib | FILE_ATTRIBUTE_SYSTEM); if (isCopy) { _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, _scratchDocDefault); /* for saveAs it's not necessary since this action is for the "current" directory, so we let manage in SAVEPOINTREACHED event generic_string backupFilePath = buffer->getBackupFileName(); if (not backupFilePath.empty()) { // delete backup file generic_string file2Delete = buffer->getBackupFileName(); buffer->setBackupFileName(generic_string()); ::DeleteFile(file2Delete.c_str()); } */ // set to signaled state via destructor EventReset. return true; //all done } buffer->setFileName(fullpath, language); buffer->setDirty(false); buffer->setStatus(DOC_REGULAR); buffer->checkFileState(); _pscratchTilla->execute(SCI_SETSAVEPOINT); _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, _scratchDocDefault); generic_string backupFilePath = buffer->getBackupFileName(); if (not backupFilePath.empty()) { // delete backup file buffer->setBackupFileName(generic_string()); ::DeleteFile(backupFilePath.c_str()); } // set to signaled state via destructor EventReset. return true; } // set to signaled state via destructor EventReset. return false; }