diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:11:33 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:11:33 +0200 |
commit | 6fcfe73ca751f068978ce201094b17cf2dfe4d20 (patch) | |
tree | e7d85e8f9057430b480cd0e2f5ccb43c9d2ef8f8 /shared | |
parent | 3.15 (diff) | |
download | FreeFileSync-6fcfe73ca751f068978ce201094b17cf2dfe4d20.tar.gz FreeFileSync-6fcfe73ca751f068978ce201094b17cf2dfe4d20.tar.bz2 FreeFileSync-6fcfe73ca751f068978ce201094b17cf2dfe4d20.zip |
3.16
Diffstat (limited to 'shared')
-rw-r--r-- | shared/dir_name.cpp | 10 | ||||
-rw-r--r-- | shared/file_handling.cpp | 719 | ||||
-rw-r--r-- | shared/file_handling.h | 10 | ||||
-rw-r--r-- | shared/i18n.cpp | 553 | ||||
-rw-r--r-- | shared/i18n_no_BOM.cpp | 553 | ||||
-rw-r--r-- | shared/recycler.cpp | 7 | ||||
-rw-r--r-- | shared/recycler.h | 2 |
7 files changed, 1245 insertions, 609 deletions
diff --git a/shared/dir_name.cpp b/shared/dir_name.cpp index 3b0c3e82..977751f5 100644 --- a/shared/dir_name.cpp +++ b/shared/dir_name.cpp @@ -134,10 +134,10 @@ private: using ffs3::DirectoryNameMainDlg; DirectoryNameMainDlg::DirectoryNameMainDlg(wxWindow& dropWindow1, - wxWindow& dropWindow2, - wxDirPickerCtrl& dirPicker, - wxComboBox& dirName, - wxStaticBoxSizer& staticBox) : + wxWindow& dropWindow2, + wxDirPickerCtrl& dirPicker, + wxComboBox& dirName, + wxStaticBoxSizer& staticBox) : dropWindow1_(dropWindow1), dropWindow2_(dropWindow2), dirPicker_(dirPicker), @@ -216,7 +216,7 @@ using ffs3::DirectoryName; DirectoryName::DirectoryName(wxWindow& dropWindow, wxDirPickerCtrl& dirPicker, wxTextCtrl& dirName, - wxStaticBoxSizer* staticBox) : + wxStaticBoxSizer* staticBox) : dropWindow_(dropWindow), dirPicker_(dirPicker), dirName_(dirName), diff --git a/shared/file_handling.cpp b/shared/file_handling.cpp index 97c4d077..178fe26f 100644 --- a/shared/file_handling.cpp +++ b/shared/file_handling.cpp @@ -241,7 +241,7 @@ ffs3::ResponseSameVol ffs3::onSameVolume(const Zstring& folderLeft, const Zstrin } -void ffs3::removeFile(const Zstring& filename) //throw (FileError); +bool ffs3::removeFile(const Zstring& filename) //throw (FileError); { #ifdef FFS_WIN //remove file, support for \\?\-prefix @@ -260,7 +260,7 @@ void ffs3::removeFile(const Zstring& filename) //throw (FileError); //now try again... if (::DeleteFile(filenameFmt.c_str())) - return; + return true; } #endif //eval error code before next call @@ -270,10 +270,11 @@ void ffs3::removeFile(const Zstring& filename) //throw (FileError); //perf: place check in error handling block //warning: this call changes error code!! if (!somethingExists(filename)) - return; //neither file nor any other object (e.g. broken symlink) with that name existing + return false; //neither file nor any other object (e.g. broken symlink) with that name existing throw FileError(errorMessage); } + return true; } @@ -593,7 +594,7 @@ struct RemoveCallbackImpl : public ffs3::CallbackRemoveDir targetDir_(targetDir), moveCallback_(moveCallback) {} - virtual void requestUiRefresh(const Zstring& currentObject) + virtual void notifyDeletion(const Zstring& currentObject) { switch (moveCallback_.requestUiRefresh(sourceDir_)) { @@ -765,6 +766,7 @@ void ffs3::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\""); throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted()); } + if (callback) callback->notifyDeletion(directory); //once per symlink return; } @@ -775,21 +777,16 @@ void ffs3::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback FilesDirsOnlyTraverser traverser(fileList, dirList); ffs3::traverseFolder(directory, false, traverser); //don't follow symlinks - //delete files for (std::vector<Zstring>::const_iterator i = fileList.begin(); i != fileList.end(); ++i) { - if (callback) callback->requestUiRefresh(*i); //call once per file - removeFile(*i); + const bool workDone = removeFile(*i); + if (callback && workDone) callback->notifyDeletion(*i); //call once per file } //delete directories recursively for (std::vector<Zstring>::const_iterator i = dirList.begin(); i != dirList.end(); ++i) - { - if (callback) callback->requestUiRefresh(*i); //and once per folder removeDirectory(*i, callback); //call recursively to correctly handle symbolic links - } - //parent directory is deleted last #ifdef FFS_WIN @@ -801,6 +798,7 @@ void ffs3::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\""); throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted()); } + if (callback) callback->notifyDeletion(directory); //and once per folder } @@ -1557,7 +1555,10 @@ void rawCopyWinApi(const Zstring& sourceFile, } if (lastError == ERROR_PATH_NOT_FOUND) + { + guardTarget.Dismiss(); //not relevant throw ErrorTargetPathMissing(errorMessage); + } try //add more meaningful message { @@ -1579,360 +1580,343 @@ void rawCopyWinApi(const Zstring& sourceFile, } -void rawCopyWinOptimized(const Zstring& sourceFile, - const Zstring& targetFile, - CallbackCopyFile* callback) //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked) -{ - /* - BackupRead() FileRead() CopyFileEx() - -------------------------------------------- - Attributes NO NO YES - create time NO NO NO - ADS YES NO YES - Encrypted NO(silent fail) NO YES - Compressed NO NO NO - Sparse YES NO NO - PERF 6% faster - - - Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with: BackupRead() FileRead() - */ - - //open sourceFile for reading - HANDLE hFileIn = ::CreateFile(ffs3::applyLongPathPrefix(sourceFile).c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //all shared modes are required to read files that are open in other applications - NULL, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL); - if (hFileIn == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); - const wxString& errorMessage = wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError); - - //if file is locked (try to) use Windows Volume Shadow Copy Service - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) - throw ErrorFileLocked(errorMessage); - - throw FileError(errorMessage); - } - Loki::ScopeGuard dummy1 = Loki::MakeGuard(::CloseHandle, hFileIn); - (void)dummy1; //silence warning "unused variable" - - - BY_HANDLE_FILE_INFORMATION infoFileIn = {}; - if (!::GetFileInformationByHandle(hFileIn, &infoFileIn)) - { - const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\""); - throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted()); - } - - const bool sourceIsCompressed = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; - const bool sourceIsSparse = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0; - const bool sourceIsEncrypted = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; - - bool targetSupportSparse = false; - bool targetSupportCompressed = false; - bool targetSupportEncryption = false; - - if (sourceIsCompressed || sourceIsSparse || sourceIsEncrypted) - { - DWORD fsFlags = 0; - - { - const size_t bufferSize = std::max(targetFile.size(), static_cast<size_t>(10000)); - boost::scoped_array<wchar_t> buffer(new wchar_t[bufferSize]); - - //suprisingly slow: ca. 1.5 ms per call! - if (!::GetVolumePathName(targetFile.c_str(), //__in LPCTSTR lpszFileName, -> seems to be no need for path prefix (check passed) - buffer.get(), //__out LPTSTR lpszVolumePathName, - static_cast<DWORD>(bufferSize))) //__in DWORD cchBufferLength - throw FileError(wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted() + - wxT("\nFailed to determine volume name!")); - - Zstring volumePath = buffer.get(); - if (!volumePath.EndsWith(common::FILE_NAME_SEPARATOR)) - volumePath += common::FILE_NAME_SEPARATOR; - - //suprisingly fast: ca. 0.03 ms per call! - if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - NULL, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, - NULL, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - throw FileError(wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(volumePath) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted() + - wxT("\nFailed to determine volume information!")); - } - - targetSupportSparse = (fsFlags & FILE_SUPPORTS_SPARSE_FILES) != 0; - targetSupportCompressed = (fsFlags & FILE_FILE_COMPRESSION ) != 0; - targetSupportEncryption = (fsFlags & FILE_SUPPORTS_ENCRYPTION ) != 0; - } - - //####################################### DST hack ########################################### - if (dst::isFatDrive(sourceFile)) //throw() - { - const dst::RawTime rawTime(infoFileIn.ftCreationTime, infoFileIn.ftLastWriteTime); - if (dst::fatHasUtcEncoded(rawTime)) //throw (std::runtime_error) - { - infoFileIn.ftLastWriteTime = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error) - ::GetSystemTimeAsFileTime(&infoFileIn.ftCreationTime); //real creation time information is not available... - } - } - - if (dst::isFatDrive(targetFile)) //throw() - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(infoFileIn.ftLastWriteTime); //throw (std::runtime_error) - infoFileIn.ftCreationTime = encodedTime.createTimeRaw; - infoFileIn.ftLastWriteTime = encodedTime.writeTimeRaw; - } - //####################################### DST hack ########################################### - - const DWORD validAttribs = FILE_ATTRIBUTE_READONLY | - FILE_ATTRIBUTE_HIDDEN | - FILE_ATTRIBUTE_SYSTEM | - FILE_ATTRIBUTE_ARCHIVE | //those two are not set properly (not worse than ::CopyFileEx()) - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | // - (targetSupportEncryption ? FILE_ATTRIBUTE_ENCRYPTED : 0); - - //create targetFile and open it for writing - HANDLE hFileOut = ::CreateFile(ffs3::applyLongPathPrefix(targetFile).c_str(), - GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - CREATE_NEW, - (infoFileIn.dwFileAttributes & validAttribs) | FILE_FLAG_SEQUENTIAL_SCAN, - NULL); - if (hFileOut == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); - const wxString& errorMessage = wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted(lastError); - - if (lastError == ERROR_FILE_EXISTS) - throw ErrorTargetExisting(errorMessage); - - if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMessage); - - throw FileError(errorMessage); - } - Loki::ScopeGuard guardTarget = Loki::MakeGuard(&removeFile, targetFile); //transactional behavior: guard just after opening target and before managing hFileOut - - Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hFileOut); - (void)dummy; //silence warning "unused variable" - - - const bool useBackupFun = !sourceIsEncrypted; //http://msdn.microsoft.com/en-us/library/aa362509(v=VS.85).aspx - - if (sourceIsCompressed && targetSupportCompressed) - { - USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; - - DWORD bytesReturned = 0; - if (!DeviceIoControl(hFileOut, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - NULL, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - NULL)) //OVERLAPPED structure - throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted() + - wxT("\nFailed to write NTFS compressed attribute!")); - } - - //although it seems the sparse attribute is set automatically by BackupWrite, we are required to do this manually: http://support.microsoft.com/kb/271398/en-us - if (sourceIsSparse && targetSupportSparse) - { - if (useBackupFun) - { - DWORD bytesReturned = 0; - if (!DeviceIoControl(hFileOut, //handle to file - FSCTL_SET_SPARSE, //dwIoControlCode - NULL, //input buffer - 0, //size of input buffer - NULL, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - NULL)) //OVERLAPPED structure - throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted() + - wxT("\nFailed to write NTFS sparse attribute!")); - } - } - - - const DWORD BUFFER_SIZE = 512 * 1024; //512 kb seems to be a reasonable buffer size - static const boost::scoped_array<BYTE> memory(new BYTE[BUFFER_SIZE]); - - struct ManageCtxt //manage context for BackupRead()/BackupWrite() - { - ManageCtxt() : read(NULL), write(NULL) {} - ~ManageCtxt() - { - if (read != NULL) - ::BackupRead (0, NULL, 0, NULL, true, false, &read); - if (write != NULL) - ::BackupWrite(0, NULL, 0, NULL, true, false, &write); - } - - LPVOID read; - LPVOID write; - } context; - - //copy contents of sourceFile to targetFile - wxULongLong totalBytesTransferred; - - bool eof = false; - do - { - DWORD bytesRead = 0; - - if (useBackupFun) - { - if (!::BackupRead(hFileIn, //__in HANDLE hFile, - memory.get(), //__out LPBYTE lpBuffer, - BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, - &bytesRead, //__out LPDWORD lpNumberOfBytesRead, - false, //__in BOOL bAbort, - false, //__in BOOL bProcessSecurity, - &context.read)) //__out LPVOID *lpContext - throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted()); - } - else if (!::ReadFile(hFileIn, //__in HANDLE hFile, - memory.get(), //__out LPVOID lpBuffer, - BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, - &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, - NULL)) //__inout_opt LPOVERLAPPED lpOverlapped - throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted()); - - if (bytesRead > BUFFER_SIZE) - throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + - wxT("\n\n") + wxT("buffer overflow")); - - if (bytesRead < BUFFER_SIZE) - eof = true; - - DWORD bytesWritten = 0; - - if (useBackupFun) - { - if (!::BackupWrite(hFileOut, //__in HANDLE hFile, - memory.get(), //__in LPBYTE lpBuffer, - bytesRead, //__in DWORD nNumberOfBytesToWrite, - &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten, - false, //__in BOOL bAbort, - false, //__in BOOL bProcessSecurity, - &context.write)) //__out LPVOID *lpContext - throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message! - } - else if (!::WriteFile(hFileOut, //__in HANDLE hFile, - memory.get(), //__out LPVOID lpBuffer, - bytesRead, //__in DWORD nNumberOfBytesToWrite, - &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, - NULL)) //__inout_opt LPOVERLAPPED lpOverlapped - throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message! - - if (bytesWritten != bytesRead) - throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + wxT("incomplete write")); - - totalBytesTransferred += bytesRead; - - //invoke callback method to update progress indicators - if (callback != NULL) - switch (callback->updateCopyStatus(totalBytesTransferred)) - { - case CallbackCopyFile::CONTINUE: - break; - - case CallbackCopyFile::CANCEL: //a user aborted operation IS an error condition! - throw FileError(wxString(_("Error copying file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + - zToWx(targetFile) + wxT("\"\n\n") + _("Operation aborted!")); - } - } - while (!eof); - - - if (totalBytesTransferred == 0) //BackupRead silently fails reading encrypted files -> double check! - { - LARGE_INTEGER inputSize = {}; - if (!::GetFileSizeEx(hFileIn, &inputSize)) - throw FileError(wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted()); - - if (inputSize.QuadPart != 0) - throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + - wxT("\n\n") + wxT("unknown error")); - } - - //time needs to be set at the end: BackupWrite() changes file time - if (!::SetFileTime(hFileOut, - &infoFileIn.ftCreationTime, - NULL, - &infoFileIn.ftLastWriteTime)) - throw FileError(wxString(_("Error changing modification time:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + - wxT("\n\n") + ffs3::getLastErrorFormatted()); - - -#ifndef NDEBUG //dst hack: verify data written - if (dst::isFatDrive(targetFile)) //throw() - { - WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; - assert(::GetFileAttributesEx(applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpFileName, - GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - &debugeAttr)); //__out LPVOID lpFileInformation - - assert(::CompareFileTime(&debugeAttr.ftCreationTime, &infoFileIn.ftCreationTime) == 0); - assert(::CompareFileTime(&debugeAttr.ftLastWriteTime, &infoFileIn.ftLastWriteTime) == 0); - } -#endif - - guardTarget.Dismiss(); - - /* - //create test sparse file - size_t sparseSize = 50 * 1024 * 1024; - HANDLE hSparse = ::CreateFile(L"C:\\sparse.file", - GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - CREATE_NEW, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL); - DWORD br = 0; - if (!::DeviceIoControl(hSparse, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &br,NULL)) - throw 1; - - LARGE_INTEGER liDistanceToMove = {}; - liDistanceToMove.QuadPart = sparseSize; - if (!::SetFilePointerEx(hSparse, liDistanceToMove, NULL, FILE_BEGIN)) - throw 1; - - if (!SetEndOfFile(hSparse)) - throw 1; - - FILE_ZERO_DATA_INFORMATION zeroInfo = {}; - zeroInfo.BeyondFinalZero.QuadPart = sparseSize; - if (!::DeviceIoControl(hSparse, FSCTL_SET_ZERO_DATA, &zeroInfo, sizeof(zeroInfo), NULL, 0, &br, NULL)) - throw 1; - - ::CloseHandle(hSparse); - */ -} +//void rawCopyWinOptimized(const Zstring& sourceFile, +// const Zstring& targetFile, +// CallbackCopyFile* callback) //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked) +//{ +// /* +// BackupRead() FileRead() CopyFileEx() +// -------------------------------------------- +// Attributes NO NO YES +// create time NO NO NO +// ADS YES NO YES +// Encrypted NO(silent fail) NO YES +// Compressed NO NO NO +// Sparse YES NO NO +// PERF 6% faster - +// +// Mark stream as compressed: FSCTL_SET_COMPRESSION +// compatible with: BackupRead() FileRead() +// */ +// +// //open sourceFile for reading +// HANDLE hFileIn = ::CreateFile(ffs3::applyLongPathPrefix(sourceFile).c_str(), +// GENERIC_READ, +// FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //all shared modes are required to read files that are open in other applications +// NULL, +// OPEN_EXISTING, +// FILE_FLAG_SEQUENTIAL_SCAN, +// NULL); +// if (hFileIn == INVALID_HANDLE_VALUE) +// { +// const DWORD lastError = ::GetLastError(); +// const wxString& errorMessage = wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError); +// +// //if file is locked (try to) use Windows Volume Shadow Copy Service +// if (lastError == ERROR_SHARING_VIOLATION || +// lastError == ERROR_LOCK_VIOLATION) +// throw ErrorFileLocked(errorMessage); +// +// throw FileError(errorMessage); +// } +// Loki::ScopeGuard dummy1 = Loki::MakeGuard(::CloseHandle, hFileIn); +// (void)dummy1; //silence warning "unused variable" +// +// +// BY_HANDLE_FILE_INFORMATION infoFileIn = {}; +// if (!::GetFileInformationByHandle(hFileIn, &infoFileIn)) +// { +// const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\""); +// throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted()); +// } +// +// //####################################### DST hack ########################################### +// if (dst::isFatDrive(sourceFile)) //throw() +// { +// const dst::RawTime rawTime(infoFileIn.ftCreationTime, infoFileIn.ftLastWriteTime); +// if (dst::fatHasUtcEncoded(rawTime)) //throw (std::runtime_error) +// { +// infoFileIn.ftLastWriteTime = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error) +// ::GetSystemTimeAsFileTime(&infoFileIn.ftCreationTime); //real creation time information is not available... +// } +// } +// +// if (dst::isFatDrive(targetFile)) //throw() +// { +// const dst::RawTime encodedTime = dst::fatEncodeUtcTime(infoFileIn.ftLastWriteTime); //throw (std::runtime_error) +// infoFileIn.ftCreationTime = encodedTime.createTimeRaw; +// infoFileIn.ftLastWriteTime = encodedTime.writeTimeRaw; +// } +// //####################################### DST hack ########################################### +// +// const DWORD validAttribs = FILE_ATTRIBUTE_READONLY | +// FILE_ATTRIBUTE_HIDDEN | +// FILE_ATTRIBUTE_SYSTEM | +// FILE_ATTRIBUTE_ARCHIVE | //those two are not set properly (not worse than ::CopyFileEx()) +// FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | // +// FILE_ATTRIBUTE_ENCRYPTED; +// +// //create targetFile and open it for writing +// HANDLE hFileOut = ::CreateFile(ffs3::applyLongPathPrefix(targetFile).c_str(), +// GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION +// FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, +// NULL, +// CREATE_NEW, +// (infoFileIn.dwFileAttributes & validAttribs) | FILE_FLAG_SEQUENTIAL_SCAN, +// NULL); +// if (hFileOut == INVALID_HANDLE_VALUE) +// { +// const DWORD lastError = ::GetLastError(); +// const wxString& errorMessage = wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted(lastError); +// +// if (lastError == ERROR_FILE_EXISTS) +// throw ErrorTargetExisting(errorMessage); +// +// if (lastError == ERROR_PATH_NOT_FOUND) +// throw ErrorTargetPathMissing(errorMessage); +// +// throw FileError(errorMessage); +// } +// Loki::ScopeGuard guardTarget = Loki::MakeGuard(&removeFile, targetFile); //transactional behavior: guard just after opening target and before managing hFileOut +// +// Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hFileOut); +// (void)dummy; //silence warning "unused variable" +// +// +//#ifndef _MSC_VER +//#warning teste perf von GetVolumeInformationByHandleW +//#endif +// DWORD fsFlags = 0; +// if (!GetVolumeInformationByHandleW(hFileOut, //__in HANDLE hFile, +// NULL, //__out_opt LPTSTR lpVolumeNameBuffer, +// 0, //__in DWORD nVolumeNameSize, +// NULL, //__out_opt LPDWORD lpVolumeSerialNumber, +// NULL, //__out_opt LPDWORD lpMaximumComponentLength, +// &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, +// NULL, //__out LPTSTR lpFileSystemNameBuffer, +// 0)) //__in DWORD nFileSystemNameSize +// { +// const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\""); +// throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted()); +// } +// +// const bool sourceIsEncrypted = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; +// const bool sourceIsCompressed = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; +// const bool sourceIsSparse = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0; +// +// bool targetSupportSparse = (fsFlags & FILE_SUPPORTS_SPARSE_FILES) != 0; +// bool targetSupportCompressed = (fsFlags & FILE_FILE_COMPRESSION ) != 0; +// bool targetSupportStreams = (fsFlags & FILE_NAMED_STREAMS ) != 0; +// +// +// const bool useBackupFun = !sourceIsEncrypted; //http://msdn.microsoft.com/en-us/library/aa362509(v=VS.85).aspx +// +// if (sourceIsCompressed && targetSupportCompressed) +// { +// USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; +// +// DWORD bytesReturned = 0; +// if (!DeviceIoControl(hFileOut, //handle to file or directory +// FSCTL_SET_COMPRESSION, //dwIoControlCode +// &cmpState, //input buffer +// sizeof(cmpState), //size of input buffer +// NULL, //lpOutBuffer +// 0, //OutBufferSize +// &bytesReturned, //number of bytes returned +// NULL)) //OVERLAPPED structure +// throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted() + +// wxT("\nFailed to write NTFS compressed attribute!")); +// } +// +// //although it seems the sparse attribute is set automatically by BackupWrite, we are required to do this manually: http://support.microsoft.com/kb/271398/en-us +// if (sourceIsSparse && targetSupportSparse) +// { +// if (useBackupFun) +// { +// DWORD bytesReturned = 0; +// if (!DeviceIoControl(hFileOut, //handle to file +// FSCTL_SET_SPARSE, //dwIoControlCode +// NULL, //input buffer +// 0, //size of input buffer +// NULL, //lpOutBuffer +// 0, //OutBufferSize +// &bytesReturned, //number of bytes returned +// NULL)) //OVERLAPPED structure +// throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted() + +// wxT("\nFailed to write NTFS sparse attribute!")); +// } +// } +// +// +// const DWORD BUFFER_SIZE = 512 * 1024; //512 kb seems to be a reasonable buffer size +// static const boost::scoped_array<BYTE> memory(new BYTE[BUFFER_SIZE]); +// +// struct ManageCtxt //manage context for BackupRead()/BackupWrite() +// { +// ManageCtxt() : read(NULL), write(NULL) {} +// ~ManageCtxt() +// { +// if (read != NULL) +// ::BackupRead (0, NULL, 0, NULL, true, false, &read); +// if (write != NULL) +// ::BackupWrite(0, NULL, 0, NULL, true, false, &write); +// } +// +// LPVOID read; +// LPVOID write; +// } context; +// +// //copy contents of sourceFile to targetFile +// wxULongLong totalBytesTransferred; +// +// bool eof = false; +// do +// { +// DWORD bytesRead = 0; +// +// if (useBackupFun) +// { +// if (!::BackupRead(hFileIn, //__in HANDLE hFile, +// memory.get(), //__out LPBYTE lpBuffer, +// BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, +// &bytesRead, //__out LPDWORD lpNumberOfBytesRead, +// false, //__in BOOL bAbort, +// false, //__in BOOL bProcessSecurity, +// &context.read)) //__out LPVOID *lpContext +// throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted()); +// } +// else if (!::ReadFile(hFileIn, //__in HANDLE hFile, +// memory.get(), //__out LPVOID lpBuffer, +// BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, +// &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, +// NULL)) //__inout_opt LPOVERLAPPED lpOverlapped +// throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted()); +// +// if (bytesRead > BUFFER_SIZE) +// throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + +// wxT("\n\n") + wxT("buffer overflow")); +// +// if (bytesRead < BUFFER_SIZE) +// eof = true; +// +// DWORD bytesWritten = 0; +// +// if (useBackupFun) +// { +// if (!::BackupWrite(hFileOut, //__in HANDLE hFile, +// memory.get(), //__in LPBYTE lpBuffer, +// bytesRead, //__in DWORD nNumberOfBytesToWrite, +// &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten, +// false, //__in BOOL bAbort, +// false, //__in BOOL bProcessSecurity, +// &context.write)) //__out LPVOID *lpContext +// throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message! +// } +// else if (!::WriteFile(hFileOut, //__in HANDLE hFile, +// memory.get(), //__out LPVOID lpBuffer, +// bytesRead, //__in DWORD nNumberOfBytesToWrite, +// &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, +// NULL)) //__inout_opt LPOVERLAPPED lpOverlapped +// throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message! +// +// if (bytesWritten != bytesRead) +// throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + wxT("incomplete write")); +// +// totalBytesTransferred += bytesRead; +// +//#ifndef _MSC_VER +//#warning totalBytesTransferred kann größer als filesize sein!! +//#endif +// +// //invoke callback method to update progress indicators +// if (callback != NULL) +// switch (callback->updateCopyStatus(totalBytesTransferred)) +// { +// case CallbackCopyFile::CONTINUE: +// break; +// +// case CallbackCopyFile::CANCEL: //a user aborted operation IS an error condition! +// throw FileError(wxString(_("Error copying file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + +// zToWx(targetFile) + wxT("\"\n\n") + _("Operation aborted!")); +// } +// } +// while (!eof); +// +// +// if (totalBytesTransferred == 0) //BackupRead silently fails reading encrypted files -> double check! +// { +// LARGE_INTEGER inputSize = {}; +// if (!::GetFileSizeEx(hFileIn, &inputSize)) +// throw FileError(wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted()); +// +// if (inputSize.QuadPart != 0) +// throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\"") + +// wxT("\n\n") + wxT("unknown error")); +// } +// +// //time needs to be set at the end: BackupWrite() changes file time +// if (!::SetFileTime(hFileOut, +// &infoFileIn.ftCreationTime, +// NULL, +// &infoFileIn.ftLastWriteTime)) +// throw FileError(wxString(_("Error changing modification time:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"") + +// wxT("\n\n") + ffs3::getLastErrorFormatted()); +// +// +//#ifndef NDEBUG //dst hack: verify data written +// if (dst::isFatDrive(targetFile)) //throw() +// { +// WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; +// assert(::GetFileAttributesEx(applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpFileName, +// GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, +// &debugeAttr)); //__out LPVOID lpFileInformation +// +// assert(::CompareFileTime(&debugeAttr.ftCreationTime, &infoFileIn.ftCreationTime) == 0); +// assert(::CompareFileTime(&debugeAttr.ftLastWriteTime, &infoFileIn.ftLastWriteTime) == 0); +// } +//#endif +// +// guardTarget.Dismiss(); +// +// /* +// //create test sparse file +// size_t sparseSize = 50 * 1024 * 1024; +// HANDLE hSparse = ::CreateFile(L"C:\\sparse.file", +// GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION +// FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, +// NULL, +// CREATE_NEW, +// FILE_FLAG_SEQUENTIAL_SCAN, +// NULL); +// DWORD br = 0; +// if (!::DeviceIoControl(hSparse, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &br,NULL)) +// throw 1; +// +// LARGE_INTEGER liDistanceToMove = {}; +// liDistanceToMove.QuadPart = sparseSize; +// if (!::SetFilePointerEx(hSparse, liDistanceToMove, NULL, FILE_BEGIN)) +// throw 1; +// +// if (!SetEndOfFile(hSparse)) +// throw 1; +// +// FILE_ZERO_DATA_INFORMATION zeroInfo = {}; +// zeroInfo.BeyondFinalZero.QuadPart = sparseSize; +// if (!::DeviceIoControl(hSparse, FSCTL_SET_ZERO_DATA, &zeroInfo, sizeof(zeroInfo), NULL, 0, &br, NULL)) +// throw 1; +// +// ::CloseHandle(hSparse); +// */ +//} #endif @@ -2005,10 +1989,11 @@ void copyFileImpl(const Zstring& sourceFile, Compressed NO YES Sparse NO YES PERF - 6% faster + SAMBA, ect. YES UNKNOWN! */ - //rawCopyWinApi(sourceFile, targetFile, callback); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked) - rawCopyWinOptimized(sourceFile, targetFile, callback); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked) ->about 8% faster + rawCopyWinApi(sourceFile, targetFile, callback); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked) + //rawCopyWinOptimized(sourceFile, targetFile, callback); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked) ->about 8% faster #elif defined FFS_LINUX rawCopyStream(sourceFile, targetFile, callback); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting) diff --git a/shared/file_handling.h b/shared/file_handling.h index 1d68c399..8867bb04 100644 --- a/shared/file_handling.h +++ b/shared/file_handling.h @@ -19,9 +19,9 @@ struct CallbackMoveFile; struct CallbackCopyFile; -bool fileExists( const Zstring& filename); //throw() replaces wxFileExists() -bool dirExists( const Zstring& dirname); //throw() replaces wxDirExists() -bool symlinkExists( const Zstring& objname); //throw() check whether a symbolic link exists +bool fileExists (const Zstring& filename); //throw() replaces wxFileExists() +bool dirExists (const Zstring& dirname); //throw() replaces wxDirExists() +bool symlinkExists (const Zstring& objname); //throw() check whether a symbolic link exists bool somethingExists(const Zstring& objname); //throw() check whether any object with this name exists //check whether two folders are located on the same (logical) volume @@ -42,7 +42,7 @@ wxULongLong getFilesize(const Zstring& filename); //throw (FileError) //file handling -void removeFile(const Zstring& filename); //throw (FileError) +bool removeFile(const Zstring& filename); //return "true" if file was actually deleted; throw (FileError) void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = NULL); //throw (FileError) @@ -85,7 +85,7 @@ void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, SymlinkTy struct CallbackRemoveDir { virtual ~CallbackRemoveDir() {} - virtual void requestUiRefresh(const Zstring& currentObject) = 0; + virtual void notifyDeletion(const Zstring& currentObject) = 0; }; diff --git a/shared/i18n.cpp b/shared/i18n.cpp index 537d9fb4..8e0bc22c 100644 --- a/shared/i18n.cpp +++ b/shared/i18n.cpp @@ -16,6 +16,7 @@ #include <boost/any.hpp> #include <boost/shared_ptr.hpp> #include <list> +#include <iterator> using ffs3::LocalizationInfo; @@ -552,51 +553,79 @@ int ffs3::retrieveSystemLanguage() - +//http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html +//http://translate.sourceforge.net/wiki/l10n/pluralforms /* -typedef Zbase<wchar_t> Wstring; +Plural forms parser: Grammar +---------------------------- +expression: + conditional-expression + +conditional-expression: + logical-or-expression + logical-or-expression ? expression : expression + +logical-or-expression: + logical-and-expression + logical-or-expression || logical-and-expression + +logical-and-expression: + equality-expression + logical-and-expression && equality-expression + +equality-expression: + relational-expression + relational-expression == relational-expression + relational-expression != relational-expression + +relational-expression: + multiplicative-expression + multiplicative-expression > multiplicative-expression + multiplicative-expression < multiplicative-expression + multiplicative-expression >= multiplicative-expression + multiplicative-expression <= multiplicative-expression + +multiplicative-expression: + pm-expression + multiplicative-expression % pm-expression + +pm-expression: + N + Number + ( Expression ) +*/ -const Wstring TK_TERNARY_QUEST = L"?"; -const Wstring TK_TERNARY_COLON = L":"; -const Wstring TK_OR = L"||"; -const Wstring TK_AND = L"&&"; -const Wstring TK_EQUAL = L"=="; -const Wstring TK_NOT_EQUAL = L"!="; -const Wstring TK_LESS = L"<"; -const Wstring TK_LESS_EQUAL = L"<="; -const Wstring TK_GREATER = L">"; -const Wstring TK_GREATER_EQUAL = L">="; -const Wstring TK_MODULUS = L"%"; -const Wstring TK_N = L"n"; -const Wstring TK_BRACKET_LEFT = L"("; -const Wstring TK_BRACKET_RIGHT = L")"; -const Wstring TK_FORM_COUNT = L"nplurals="; -const Wstring TK_PHRASE_BEGIN = L"plural="; +//expression interface +struct Expression { virtual ~Expression() {} }; + template <class T> -struct Expr +struct Expr : public Expression { typedef T ValueType; virtual ValueType eval() const = 0; }; - -template <class In, class Out, class BiOp> -struct GenericBiExp : public Out +//specific binary expression based on STL function objects +template <class StlOp> +struct BinaryExp : public Expr<typename StlOp::result_type> { - GenericBiExp(const In& lhs, const In& rhs, BiOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {} - virtual typename Out::ValueType eval() const { return biop_(lhs_.eval(), rhs_.eval()); } - const In& lhs_; - const In& rhs_; - BiOp biop_; + typedef const Expr<typename StlOp::first_argument_type> SourceExp; + + BinaryExp(const SourceExp& lhs, const SourceExp& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {} + virtual typename StlOp::result_type eval() const { return biop_(lhs_.eval(), rhs_.eval()); } + const SourceExp& lhs_; + const SourceExp& rhs_; + StlOp biop_; }; -template <class Out, class In, class BiOp> +template <class StlOp> inline -GenericBiExp<In, Out, BiOp> makeBiExp(const In& lhs, const In& rhs, BiOp biop) +BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw (std::bad_cast) { - return GenericBiExp<In, Out, BiOp>(lhs, rhs, biop); + return BinaryExp<StlOp>(dynamic_cast<const Expr<typename StlOp::first_argument_type >&>(lhs), + dynamic_cast<const Expr<typename StlOp::second_argument_type>&>(rhs), biop); } template <class Out> @@ -612,39 +641,33 @@ struct TernaryExp : public Out struct LiteralNumberEx : public Expr<int> { LiteralNumberEx(int n) : n_(n) {} - virtual ValueType eval() const { return n_; } + virtual int eval() const { return n_; } int n_; }; struct NumberN : public Expr<int> { NumberN(int& n) : n_(n) {} - virtual ValueType eval() const { return n_; } + virtual int eval() const { return n_; } int& n_; }; +typedef Zbase<wchar_t> Wstring; + + class PluralForm { public: - struct ParserError {}; + struct ParsingError {}; - PluralForm(const Wstring& phrase) //.po format,e.g.: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) + PluralForm(const Wstring& phrase) : n_(0) //.po format,e.g.: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) { - Wstring tmp; - std::remove_copy_if(phrase.begin(), phrase.end(), std::back_inserter(tmp), std::iswspace); - - size_t pos = tmp.find(TK_FORM_COUNT); - if (pos == Wstring::npos) - throw ParserError(); - - count = tmp.substr(pos + TK_FORM_COUNT.size()).toNumber<int>(); - - pos = tmp.find(TK_PHRASE_BEGIN); - if (pos == Wstring::npos) - throw ParserError(); - - expr = &parseNumber(tmp.substr(pos + TK_PHRASE_BEGIN.size())); + Parser(phrase, //in + expr, //out + n_, // + count, // + dump); // } int formCount() const { return count; } @@ -652,95 +675,397 @@ public: int getForm(int n) const { n_ = n ; return expr->eval(); } private: - const Expr<bool>& parseBool(const Wstring& phrase) - { - Wstring part1, part2; - if (trySplit(phrase, TK_OR, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseBool(part1), parseBool(part2), std::logical_or<bool>())); - else if (trySplit(phrase, TK_AND, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseBool(part1), parseBool(part2), std::logical_and<bool>())); - else if (trySplit(phrase, TK_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::equal_to<int>())); - else if (trySplit(phrase, TK_NOT_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::not_equal_to<int>())); - else if (trySplit(phrase, TK_LESS, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::less<int>())); - else if (trySplit(phrase, TK_LESS_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::less_equal<int>())); - else if (trySplit(phrase, TK_GREATER, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::greater<int>())); - else if (trySplit(phrase, TK_GREATER_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::greater_equal<int>())); - else - throw ParserError(); - } + typedef std::list<boost::shared_ptr<Expression> > DumpList; - const Expr<int>& parseNumber(const Wstring& phrase) + struct Token { - Wstring part1, part2; - + enum Type + { + TK_FORM_COUNT, + TK_PHRASE_BEGIN, + TK_ASSIGN, + TK_SEMICOLON, + TK_TERNARY_QUEST, + TK_TERNARY_COLON, + TK_OR, + TK_AND, + TK_EQUAL, + TK_NOT_EQUAL, + TK_LESS, + TK_LESS_EQUAL, + TK_GREATER, + TK_GREATER_EQUAL, + TK_MODULUS, + TK_N, + TK_NUMBER, + TK_BRACKET_LEFT, + TK_BRACKET_RIGHT, + TK_END + }; + + Token(Type t) : type(t), number(0) {} + + Type type; + int number; //if type == TK_NUMBER + }; - if (trySplit(phrase, TK_TERNARY_QUEST, part1, part2, TO_LEFT)) + class Scanner + { + public: + Scanner(const Wstring& phrase) : phrase_(phrase) { - Wstring part3; - if (!trySplit(Wstring(part2), TK_TERNARY_COLON, part2, part3, TO_LEFT)) - throw ParserError(); - return manageObj(TernaryExp<Expr<int> >(parseBool(part1), parseNumber(part2), parseNumber(part3))); + tokens.push_back(std::make_pair(L"nplurals", Token::TK_FORM_COUNT)); + tokens.push_back(std::make_pair(L"plural" , Token::TK_PHRASE_BEGIN)); + tokens.push_back(std::make_pair(L";" , Token::TK_SEMICOLON )); + tokens.push_back(std::make_pair(L"?" , Token::TK_TERNARY_QUEST)); + tokens.push_back(std::make_pair(L":" , Token::TK_TERNARY_COLON)); + tokens.push_back(std::make_pair(L"||", Token::TK_OR )); + tokens.push_back(std::make_pair(L"&&", Token::TK_AND )); + tokens.push_back(std::make_pair(L"==", Token::TK_EQUAL )); + tokens.push_back(std::make_pair(L"=" , Token::TK_ASSIGN )); + tokens.push_back(std::make_pair(L"!=", Token::TK_NOT_EQUAL )); + tokens.push_back(std::make_pair(L"<=", Token::TK_LESS_EQUAL )); + tokens.push_back(std::make_pair(L"<" , Token::TK_LESS )); + tokens.push_back(std::make_pair(L">=", Token::TK_GREATER_EQUAL)); + tokens.push_back(std::make_pair(L">" , Token::TK_GREATER )); + tokens.push_back(std::make_pair(L"%" , Token::TK_MODULUS )); + tokens.push_back(std::make_pair(L"n" , Token::TK_N )); + tokens.push_back(std::make_pair(L"N" , Token::TK_N )); + tokens.push_back(std::make_pair(L"(" , Token::TK_BRACKET_LEFT )); + tokens.push_back(std::make_pair(L")" , Token::TK_BRACKET_RIGHT)); } - else if (trySplit(phrase, TK_MODULUS, part1, part2)) - return manageObj(makeBiExp<Expr<int> >(parseNumber(part1), parseNumber(part2), std::modulus<int>())); - else if (phrase == TK_N) - return manageObj(NumberN(n_)); - else //expect literal number + + Token nextToken() { - if (std::find_if(phrase.begin(), phrase.end(), std::not1(std::ptr_fun(std::iswdigit))) != phrase.end()) - throw ParserError(); - return manageObj(LiteralNumberEx(phrase.toNumber<int>())); + phrase_.Trim(true, false); //remove whitespace + + if (phrase_.empty()) return Token(Token::TK_END); + + for (TokenList::const_iterator i = tokens.begin(); i != tokens.end(); ++i) + if (phrase_.StartsWith(i->first)) + { + phrase_ = phrase_.substr(i->first.size()); + return Token(i->second); + } + + Wstring::iterator digitEnd = std::find_if(phrase_.begin(), phrase_.end(), std::not1(std::ptr_fun(std::iswdigit))); + int digitCount = digitEnd - phrase_.begin(); + if (digitCount != 0) + { + Token out(Token::TK_NUMBER); + out.number = Wstring(phrase_.c_str(), digitCount).toNumber<int>(); + phrase_ = phrase_.substr(digitCount); + return out; + } + + throw ParsingError(); //unknown token } - } - enum Associativity - { - TO_LEFT, - TO_RIGHT + private: + typedef std::vector<std::pair<Wstring, Token::Type> > TokenList; + TokenList tokens; + Wstring phrase_; }; - static bool trySplit(const Wstring& phrase, const Wstring& delimiter, Wstring& part1, Wstring& part2, Associativity assoc = TO_RIGHT) - { - size_t pos = assoc == TO_LEFT ? - phrase.find(delimiter) : //reverse [!] - phrase.rfind(delimiter); - if (pos == Wstring::npos) - return false; - - part1 = phrase.substr(0, pos); - part2 = phrase.substr(pos + delimiter.size()); - return true; - } - template <class T> - const T& manageObj(const T& obj) + + class Parser { - dump.push_back(obj); - return *boost::any_cast<T>(&dump.back()); - } + public: + Parser(const Wstring& phrase, //in + const Expr<int>*& expr, int& n, int& count, PluralForm::DumpList& dump) ://out + scn(phrase), + tk(scn.nextToken()), + n_(n), + dump_(dump) + { + consumeToken(Token::TK_FORM_COUNT); + consumeToken(Token::TK_ASSIGN); + + count = token().number; + consumeToken(Token::TK_NUMBER); + + consumeToken(Token::TK_SEMICOLON); + consumeToken(Token::TK_PHRASE_BEGIN); + consumeToken(Token::TK_ASSIGN); + + try + { + const Expression& e = parse(); + expr = &dynamic_cast<const Expr<int>&>(e); + } + catch(std::bad_cast&) { throw ParsingError(); } + + consumeToken(Token::TK_END); + } + + private: + void nextToken() { tk = scn.nextToken(); } + const Token& token() const { return tk; } + + void consumeToken(Token::Type t) + { + if (token().type != t) + throw ParsingError(); + nextToken(); + } + + const Expression& parse() { return parseConditional(); }; + + const Expression& parseConditional() + { + const Expression& e = parseLogicalOr(); + + if (token().type == Token::TK_TERNARY_QUEST) + { + nextToken(); + const Expression& thenEx = parse(); //associativity: <- + consumeToken(Token::TK_TERNARY_COLON); + const Expression& elseEx = parse(); // + + return manageObj(TernaryExp<Expr<int> >(dynamic_cast<const Expr<bool>&>(e), + dynamic_cast<const Expr<int>&>(thenEx), + dynamic_cast<const Expr<int>&>(elseEx))); + } + return e; + } + + const Expression& parseLogicalOr() + { + const Expression* e = &parseLogicalAnd(); + for (;;) //associativity: -> + if (token().type == Token::TK_OR) + { + nextToken(); + const Expression& rhs = parseLogicalAnd(); + e = &manageObj(makeBiExp(*e, rhs, std::logical_or<bool>())); + } + else break; + return *e; + } + + const Expression& parseLogicalAnd() + { + const Expression* e = &parseEquality(); + for (;;) //associativity: -> + if (token().type == Token::TK_AND) + { + nextToken(); + const Expression& rhs = parseEquality(); + + e = &manageObj(makeBiExp(*e, rhs, std::logical_and<bool>())); + } + else break; + return *e; + } + + const Expression& parseEquality() + { + const Expression& e = parseRelational(); + + Token::Type t = token().type; + if (t == Token::TK_EQUAL || t == Token::TK_NOT_EQUAL) //associativity: n/a + { + nextToken(); + const Expression& rhs = parseRelational(); + + if (t == Token::TK_EQUAL) + return manageObj(makeBiExp(e, rhs, std::equal_to<int>())); + else + return manageObj(makeBiExp(e, rhs, std::not_equal_to<int>())); + } + return e; + } + + const Expression& parseRelational() + { + const Expression& e = parseMultiplicative(); + + Token::Type t = token().type; + if (t == Token::TK_LESS || //associativity: n/a + t == Token::TK_LESS_EQUAL|| + t == Token::TK_GREATER || + t == Token::TK_GREATER_EQUAL) + { + nextToken(); + const Expression& rhs = parseMultiplicative(); + + if (t == Token::TK_LESS) return manageObj(makeBiExp(e, rhs, std::less <int>())); + if (t == Token::TK_LESS_EQUAL) return manageObj(makeBiExp(e, rhs, std::less_equal <int>())); + if (t == Token::TK_GREATER) return manageObj(makeBiExp(e, rhs, std::greater <int>())); + if (t == Token::TK_GREATER_EQUAL) return manageObj(makeBiExp(e, rhs, std::greater_equal<int>())); + } + return e; + } + + const Expression& parseMultiplicative() + { + const Expression* e = &parsePrimary(); + + for (;;) //associativity: -> + if (token().type == Token::TK_MODULUS) + { + nextToken(); + const Expression& rhs = parsePrimary(); + + //"compile-time" check: n % 0 + const LiteralNumberEx* literal = dynamic_cast<const LiteralNumberEx*>(&rhs); + if (literal && literal->eval() == 0) + throw ParsingError(); + + e = &manageObj(makeBiExp(*e, rhs, std::modulus<int>())); + } + else break; + return *e; + } + + const Expression& parsePrimary() + { + if (token().type == Token::TK_N) + { + nextToken(); + return manageObj(NumberN(n_)); + } + else if (token().type == Token::TK_NUMBER) + { + const int number = token().number; + nextToken(); + return manageObj(LiteralNumberEx(number)); + } + else if (token().type == Token::TK_BRACKET_LEFT) + { + nextToken(); + const Expression& e = parse(); + + consumeToken(Token::TK_BRACKET_RIGHT); + return e; + } + else + throw ParsingError(); + } + + template <class T> + const T& manageObj(const T& obj) + { + boost::shared_ptr<Expression> newEntry(new T(obj)); + dump_.push_back(newEntry); + return static_cast<T&>(*dump_.back()); + } + + Scanner scn; + Token tk; + + int& n_; + DumpList& dump_; //manage polymorphc object lifetimes + }; - int count; const Expr<int>* expr; mutable int n_; + int count; - std::list<boost::any> dump; //manage polymorphc object lifetimes + PluralForm::DumpList dump; //manage polymorphc object lifetimes }; - PluralForm dummy(L"nplurals=3; plural=n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2"); - int ddf = dummy.formCount(); - int kddf = dummy.getForm(0); -*/ +const wchar_t formPol[] = L"nplurals=3; plural=n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2"; +int tstPol(int n) +{ + return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2; +} +const wchar_t formRu[] = L"nplurals= 3; plural=n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2"; +int tstRu(int n) +{ + return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2 ; +} + +const wchar_t formLit[] = L"nplurals =3; plural=n% 10 == 1&& n % 100 != 11 ? 0 : n % 100 != 12&& n % 10 == 2 ? 1 : 0"; +int tstLit(int n) +{ + return n % 10 == 1&& n % 100 != 11 ? 0 : n % 100 != 12&& n % 10 == 2 ? 1 : 0; +} + +const wchar_t formArab[] = L"nplurals = 6; plural = n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : (n % 100 >= 3 && n % 100 <= 10) ? 3 : (n % 100 >= 11 && n % 100 <= 99) || (n % 100 == 1) || (n % 100 ==2) ? 4 : 5"; +int tstArab(int n) +{ + return n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : (n % 100 >= 3 && n % 100 <= 10) ? 3 : (n % 100 >= 11 && n % 100 <= 99) || (n % 100 == 1) || (n % 100 ==2) ? 4 : 5; +} + +const wchar_t formGerm[] = L"nplurals=2; plural= n == 1 ? 0 : 1"; +int tstGerm(int n) +{ + return n == 1 ? 0 : 1; +} + +const wchar_t formFren[] = L"nplurals=2; plural= n <= 1 ? 0 : 1"; +int tstFren(int n) +{ + return n <= 1 ? 0 : 1; +} + +const wchar_t formJap[] = L"nplurals=1; plural=0"; +int tstJap(int n) +{ + return 0; +} + +const wchar_t formRom[] = L"nplurals=3; plural= n == 1 ? 0 : n == 0 || (n % 100 >= 1 && n % 100 <= 20) ? 1 : 2 "; +int tstRom(int n) +{ + return n == 1 ? 0 : n == 0 || (n % 100 >= 1 && n % 100 <= 20) ? 1 : 2 ; +} + +const wchar_t formCze[] = L" nplurals=3; plural= n % 100 == 1 ? 0 : n % 100 >= 2 && n % 100 <= 4 ? 1 : 2"; +int tstCze(int n) +{ + return n % 100 == 1 ? 0 : n % 100 >= 2 && n % 100 <= 4 ? 1 : 2; +} + +const wchar_t formSlov[] = L" nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3 "; +int tstSlov(int n) +{ + return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3; +} + +void unitTest() +{ + typedef int (*TestFun)(int); + typedef std::vector<std::pair<const wchar_t*, TestFun> > PhraseFunList; + PhraseFunList phrases; + phrases.push_back(std::make_pair(formPol, &tstPol)); + phrases.push_back(std::make_pair(formRu, &tstRu)); + phrases.push_back(std::make_pair(formLit, &tstLit)); + phrases.push_back(std::make_pair(formArab, &tstArab)); + phrases.push_back(std::make_pair(formGerm, &tstGerm)); + phrases.push_back(std::make_pair(formFren, &tstFren)); + phrases.push_back(std::make_pair(formJap, &tstJap)); + phrases.push_back(std::make_pair(formRom, &tstRom)); + phrases.push_back(std::make_pair(formCze, &tstCze)); + phrases.push_back(std::make_pair(formSlov, &tstSlov)); + + for (PhraseFunList::const_iterator i = phrases.begin(); i != phrases.end(); ++i) + { + PluralForm pf(i->first); + for (int j = 0; j < 10000000; ++j) + assert((i->second)(j) == pf.getForm(j)); + } +} wxString ffs3::translate(const wxString& original) //translate into currently selected language { + /* + int ba = 3; + + unitTest(); + +#ifndef _MSC_VER +#warning 3434 +#endif + + */ + + + //look for translation in buffer table const Translation::const_iterator i = activeTranslation.find(original); if (i != activeTranslation.end()) diff --git a/shared/i18n_no_BOM.cpp b/shared/i18n_no_BOM.cpp index 78f2b230..b89d3221 100644 --- a/shared/i18n_no_BOM.cpp +++ b/shared/i18n_no_BOM.cpp @@ -16,6 +16,7 @@ #include <boost/any.hpp> #include <boost/shared_ptr.hpp> #include <list> +#include <iterator> using ffs3::LocalizationInfo; @@ -552,51 +553,79 @@ int ffs3::retrieveSystemLanguage() - +//http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html +//http://translate.sourceforge.net/wiki/l10n/pluralforms /* -typedef Zbase<wchar_t> Wstring; +Plural forms parser: Grammar +---------------------------- +expression: + conditional-expression + +conditional-expression: + logical-or-expression + logical-or-expression ? expression : expression + +logical-or-expression: + logical-and-expression + logical-or-expression || logical-and-expression + +logical-and-expression: + equality-expression + logical-and-expression && equality-expression + +equality-expression: + relational-expression + relational-expression == relational-expression + relational-expression != relational-expression + +relational-expression: + multiplicative-expression + multiplicative-expression > multiplicative-expression + multiplicative-expression < multiplicative-expression + multiplicative-expression >= multiplicative-expression + multiplicative-expression <= multiplicative-expression + +multiplicative-expression: + pm-expression + multiplicative-expression % pm-expression + +pm-expression: + N + Number + ( Expression ) +*/ -const Wstring TK_TERNARY_QUEST = L"?"; -const Wstring TK_TERNARY_COLON = L":"; -const Wstring TK_OR = L"||"; -const Wstring TK_AND = L"&&"; -const Wstring TK_EQUAL = L"=="; -const Wstring TK_NOT_EQUAL = L"!="; -const Wstring TK_LESS = L"<"; -const Wstring TK_LESS_EQUAL = L"<="; -const Wstring TK_GREATER = L">"; -const Wstring TK_GREATER_EQUAL = L">="; -const Wstring TK_MODULUS = L"%"; -const Wstring TK_N = L"n"; -const Wstring TK_BRACKET_LEFT = L"("; -const Wstring TK_BRACKET_RIGHT = L")"; -const Wstring TK_FORM_COUNT = L"nplurals="; -const Wstring TK_PHRASE_BEGIN = L"plural="; +//expression interface +struct Expression { virtual ~Expression() {} }; + template <class T> -struct Expr +struct Expr : public Expression { typedef T ValueType; virtual ValueType eval() const = 0; }; - -template <class In, class Out, class BiOp> -struct GenericBiExp : public Out +//specific binary expression based on STL function objects +template <class StlOp> +struct BinaryExp : public Expr<typename StlOp::result_type> { - GenericBiExp(const In& lhs, const In& rhs, BiOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {} - virtual typename Out::ValueType eval() const { return biop_(lhs_.eval(), rhs_.eval()); } - const In& lhs_; - const In& rhs_; - BiOp biop_; + typedef const Expr<typename StlOp::first_argument_type> SourceExp; + + BinaryExp(const SourceExp& lhs, const SourceExp& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {} + virtual typename StlOp::result_type eval() const { return biop_(lhs_.eval(), rhs_.eval()); } + const SourceExp& lhs_; + const SourceExp& rhs_; + StlOp biop_; }; -template <class Out, class In, class BiOp> +template <class StlOp> inline -GenericBiExp<In, Out, BiOp> makeBiExp(const In& lhs, const In& rhs, BiOp biop) +BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw (std::bad_cast) { - return GenericBiExp<In, Out, BiOp>(lhs, rhs, biop); + return BinaryExp<StlOp>(dynamic_cast<const Expr<typename StlOp::first_argument_type >&>(lhs), + dynamic_cast<const Expr<typename StlOp::second_argument_type>&>(rhs), biop); } template <class Out> @@ -612,39 +641,33 @@ struct TernaryExp : public Out struct LiteralNumberEx : public Expr<int> { LiteralNumberEx(int n) : n_(n) {} - virtual ValueType eval() const { return n_; } + virtual int eval() const { return n_; } int n_; }; struct NumberN : public Expr<int> { NumberN(int& n) : n_(n) {} - virtual ValueType eval() const { return n_; } + virtual int eval() const { return n_; } int& n_; }; +typedef Zbase<wchar_t> Wstring; + + class PluralForm { public: - struct ParserError {}; + struct ParsingError {}; - PluralForm(const Wstring& phrase) //.po format,e.g.: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) + PluralForm(const Wstring& phrase) : n_(0) //.po format,e.g.: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) { - Wstring tmp; - std::remove_copy_if(phrase.begin(), phrase.end(), std::back_inserter(tmp), std::iswspace); - - size_t pos = tmp.find(TK_FORM_COUNT); - if (pos == Wstring::npos) - throw ParserError(); - - count = tmp.substr(pos + TK_FORM_COUNT.size()).toNumber<int>(); - - pos = tmp.find(TK_PHRASE_BEGIN); - if (pos == Wstring::npos) - throw ParserError(); - - expr = &parseNumber(tmp.substr(pos + TK_PHRASE_BEGIN.size())); + Parser(phrase, //in + expr, //out + n_, // + count, // + dump); // } int formCount() const { return count; } @@ -652,95 +675,397 @@ public: int getForm(int n) const { n_ = n ; return expr->eval(); } private: - const Expr<bool>& parseBool(const Wstring& phrase) - { - Wstring part1, part2; - if (trySplit(phrase, TK_OR, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseBool(part1), parseBool(part2), std::logical_or<bool>())); - else if (trySplit(phrase, TK_AND, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseBool(part1), parseBool(part2), std::logical_and<bool>())); - else if (trySplit(phrase, TK_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::equal_to<int>())); - else if (trySplit(phrase, TK_NOT_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::not_equal_to<int>())); - else if (trySplit(phrase, TK_LESS, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::less<int>())); - else if (trySplit(phrase, TK_LESS_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::less_equal<int>())); - else if (trySplit(phrase, TK_GREATER, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::greater<int>())); - else if (trySplit(phrase, TK_GREATER_EQUAL, part1, part2)) - return manageObj(makeBiExp<Expr<bool> >(parseNumber(part1), parseNumber(part2), std::greater_equal<int>())); - else - throw ParserError(); - } + typedef std::list<boost::shared_ptr<Expression> > DumpList; - const Expr<int>& parseNumber(const Wstring& phrase) + struct Token { - Wstring part1, part2; - + enum Type + { + TK_FORM_COUNT, + TK_PHRASE_BEGIN, + TK_ASSIGN, + TK_SEMICOLON, + TK_TERNARY_QUEST, + TK_TERNARY_COLON, + TK_OR, + TK_AND, + TK_EQUAL, + TK_NOT_EQUAL, + TK_LESS, + TK_LESS_EQUAL, + TK_GREATER, + TK_GREATER_EQUAL, + TK_MODULUS, + TK_N, + TK_NUMBER, + TK_BRACKET_LEFT, + TK_BRACKET_RIGHT, + TK_END + }; + + Token(Type t) : type(t), number(0) {} + + Type type; + int number; //if type == TK_NUMBER + }; - if (trySplit(phrase, TK_TERNARY_QUEST, part1, part2, TO_LEFT)) + class Scanner + { + public: + Scanner(const Wstring& phrase) : phrase_(phrase) { - Wstring part3; - if (!trySplit(Wstring(part2), TK_TERNARY_COLON, part2, part3, TO_LEFT)) - throw ParserError(); - return manageObj(TernaryExp<Expr<int> >(parseBool(part1), parseNumber(part2), parseNumber(part3))); + tokens.push_back(std::make_pair(L"nplurals", Token::TK_FORM_COUNT)); + tokens.push_back(std::make_pair(L"plural" , Token::TK_PHRASE_BEGIN)); + tokens.push_back(std::make_pair(L";" , Token::TK_SEMICOLON )); + tokens.push_back(std::make_pair(L"?" , Token::TK_TERNARY_QUEST)); + tokens.push_back(std::make_pair(L":" , Token::TK_TERNARY_COLON)); + tokens.push_back(std::make_pair(L"||", Token::TK_OR )); + tokens.push_back(std::make_pair(L"&&", Token::TK_AND )); + tokens.push_back(std::make_pair(L"==", Token::TK_EQUAL )); + tokens.push_back(std::make_pair(L"=" , Token::TK_ASSIGN )); + tokens.push_back(std::make_pair(L"!=", Token::TK_NOT_EQUAL )); + tokens.push_back(std::make_pair(L"<=", Token::TK_LESS_EQUAL )); + tokens.push_back(std::make_pair(L"<" , Token::TK_LESS )); + tokens.push_back(std::make_pair(L">=", Token::TK_GREATER_EQUAL)); + tokens.push_back(std::make_pair(L">" , Token::TK_GREATER )); + tokens.push_back(std::make_pair(L"%" , Token::TK_MODULUS )); + tokens.push_back(std::make_pair(L"n" , Token::TK_N )); + tokens.push_back(std::make_pair(L"N" , Token::TK_N )); + tokens.push_back(std::make_pair(L"(" , Token::TK_BRACKET_LEFT )); + tokens.push_back(std::make_pair(L")" , Token::TK_BRACKET_RIGHT)); } - else if (trySplit(phrase, TK_MODULUS, part1, part2)) - return manageObj(makeBiExp<Expr<int> >(parseNumber(part1), parseNumber(part2), std::modulus<int>())); - else if (phrase == TK_N) - return manageObj(NumberN(n_)); - else //expect literal number + + Token nextToken() { - if (std::find_if(phrase.begin(), phrase.end(), std::not1(std::ptr_fun(std::iswdigit))) != phrase.end()) - throw ParserError(); - return manageObj(LiteralNumberEx(phrase.toNumber<int>())); + phrase_.Trim(true, false); //remove whitespace + + if (phrase_.empty()) return Token(Token::TK_END); + + for (TokenList::const_iterator i = tokens.begin(); i != tokens.end(); ++i) + if (phrase_.StartsWith(i->first)) + { + phrase_ = phrase_.substr(i->first.size()); + return Token(i->second); + } + + Wstring::iterator digitEnd = std::find_if(phrase_.begin(), phrase_.end(), std::not1(std::ptr_fun(std::iswdigit))); + int digitCount = digitEnd - phrase_.begin(); + if (digitCount != 0) + { + Token out(Token::TK_NUMBER); + out.number = Wstring(phrase_.c_str(), digitCount).toNumber<int>(); + phrase_ = phrase_.substr(digitCount); + return out; + } + + throw ParsingError(); //unknown token } - } - enum Associativity - { - TO_LEFT, - TO_RIGHT + private: + typedef std::vector<std::pair<Wstring, Token::Type> > TokenList; + TokenList tokens; + Wstring phrase_; }; - static bool trySplit(const Wstring& phrase, const Wstring& delimiter, Wstring& part1, Wstring& part2, Associativity assoc = TO_RIGHT) - { - size_t pos = assoc == TO_LEFT ? - phrase.find(delimiter) : //reverse [!] - phrase.rfind(delimiter); - if (pos == Wstring::npos) - return false; - - part1 = phrase.substr(0, pos); - part2 = phrase.substr(pos + delimiter.size()); - return true; - } - template <class T> - const T& manageObj(const T& obj) + + class Parser { - dump.push_back(obj); - return *boost::any_cast<T>(&dump.back()); - } + public: + Parser(const Wstring& phrase, //in + const Expr<int>*& expr, int& n, int& count, PluralForm::DumpList& dump) ://out + scn(phrase), + tk(scn.nextToken()), + n_(n), + dump_(dump) + { + consumeToken(Token::TK_FORM_COUNT); + consumeToken(Token::TK_ASSIGN); + + count = token().number; + consumeToken(Token::TK_NUMBER); + + consumeToken(Token::TK_SEMICOLON); + consumeToken(Token::TK_PHRASE_BEGIN); + consumeToken(Token::TK_ASSIGN); + + try + { + const Expression& e = parse(); + expr = &dynamic_cast<const Expr<int>&>(e); + } + catch(std::bad_cast&) { throw ParsingError(); } + + consumeToken(Token::TK_END); + } + + private: + void nextToken() { tk = scn.nextToken(); } + const Token& token() const { return tk; } + + void consumeToken(Token::Type t) + { + if (token().type != t) + throw ParsingError(); + nextToken(); + } + + const Expression& parse() { return parseConditional(); }; + + const Expression& parseConditional() + { + const Expression& e = parseLogicalOr(); + + if (token().type == Token::TK_TERNARY_QUEST) + { + nextToken(); + const Expression& thenEx = parse(); //associativity: <- + consumeToken(Token::TK_TERNARY_COLON); + const Expression& elseEx = parse(); // + + return manageObj(TernaryExp<Expr<int> >(dynamic_cast<const Expr<bool>&>(e), + dynamic_cast<const Expr<int>&>(thenEx), + dynamic_cast<const Expr<int>&>(elseEx))); + } + return e; + } + + const Expression& parseLogicalOr() + { + const Expression* e = &parseLogicalAnd(); + for (;;) //associativity: -> + if (token().type == Token::TK_OR) + { + nextToken(); + const Expression& rhs = parseLogicalAnd(); + e = &manageObj(makeBiExp(*e, rhs, std::logical_or<bool>())); + } + else break; + return *e; + } + + const Expression& parseLogicalAnd() + { + const Expression* e = &parseEquality(); + for (;;) //associativity: -> + if (token().type == Token::TK_AND) + { + nextToken(); + const Expression& rhs = parseEquality(); + + e = &manageObj(makeBiExp(*e, rhs, std::logical_and<bool>())); + } + else break; + return *e; + } + + const Expression& parseEquality() + { + const Expression& e = parseRelational(); + + Token::Type t = token().type; + if (t == Token::TK_EQUAL || t == Token::TK_NOT_EQUAL) //associativity: n/a + { + nextToken(); + const Expression& rhs = parseRelational(); + + if (t == Token::TK_EQUAL) + return manageObj(makeBiExp(e, rhs, std::equal_to<int>())); + else + return manageObj(makeBiExp(e, rhs, std::not_equal_to<int>())); + } + return e; + } + + const Expression& parseRelational() + { + const Expression& e = parseMultiplicative(); + + Token::Type t = token().type; + if (t == Token::TK_LESS || //associativity: n/a + t == Token::TK_LESS_EQUAL|| + t == Token::TK_GREATER || + t == Token::TK_GREATER_EQUAL) + { + nextToken(); + const Expression& rhs = parseMultiplicative(); + + if (t == Token::TK_LESS) return manageObj(makeBiExp(e, rhs, std::less <int>())); + if (t == Token::TK_LESS_EQUAL) return manageObj(makeBiExp(e, rhs, std::less_equal <int>())); + if (t == Token::TK_GREATER) return manageObj(makeBiExp(e, rhs, std::greater <int>())); + if (t == Token::TK_GREATER_EQUAL) return manageObj(makeBiExp(e, rhs, std::greater_equal<int>())); + } + return e; + } + + const Expression& parseMultiplicative() + { + const Expression* e = &parsePrimary(); + + for (;;) //associativity: -> + if (token().type == Token::TK_MODULUS) + { + nextToken(); + const Expression& rhs = parsePrimary(); + + //"compile-time" check: n % 0 + const LiteralNumberEx* literal = dynamic_cast<const LiteralNumberEx*>(&rhs); + if (literal && literal->eval() == 0) + throw ParsingError(); + + e = &manageObj(makeBiExp(*e, rhs, std::modulus<int>())); + } + else break; + return *e; + } + + const Expression& parsePrimary() + { + if (token().type == Token::TK_N) + { + nextToken(); + return manageObj(NumberN(n_)); + } + else if (token().type == Token::TK_NUMBER) + { + const int number = token().number; + nextToken(); + return manageObj(LiteralNumberEx(number)); + } + else if (token().type == Token::TK_BRACKET_LEFT) + { + nextToken(); + const Expression& e = parse(); + + consumeToken(Token::TK_BRACKET_RIGHT); + return e; + } + else + throw ParsingError(); + } + + template <class T> + const T& manageObj(const T& obj) + { + boost::shared_ptr<Expression> newEntry(new T(obj)); + dump_.push_back(newEntry); + return static_cast<T&>(*dump_.back()); + } + + Scanner scn; + Token tk; + + int& n_; + DumpList& dump_; //manage polymorphc object lifetimes + }; - int count; const Expr<int>* expr; mutable int n_; + int count; - std::list<boost::any> dump; //manage polymorphc object lifetimes + PluralForm::DumpList dump; //manage polymorphc object lifetimes }; - PluralForm dummy(L"nplurals=3; plural=n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2"); - int ddf = dummy.formCount(); - int kddf = dummy.getForm(0); -*/ +const wchar_t formPol[] = L"nplurals=3; plural=n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2"; +int tstPol(int n) +{ + return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2; +} +const wchar_t formRu[] = L"nplurals= 3; plural=n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2"; +int tstRu(int n) +{ + return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 > 20) ? 1 : 2 ; +} + +const wchar_t formLit[] = L"nplurals =3; plural=n% 10 == 1&& n % 100 != 11 ? 0 : n % 100 != 12&& n % 10 == 2 ? 1 : 0"; +int tstLit(int n) +{ + return n % 10 == 1&& n % 100 != 11 ? 0 : n % 100 != 12&& n % 10 == 2 ? 1 : 0; +} + +const wchar_t formArab[] = L"nplurals = 6; plural = n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : (n % 100 >= 3 && n % 100 <= 10) ? 3 : (n % 100 >= 11 && n % 100 <= 99) || (n % 100 == 1) || (n % 100 ==2) ? 4 : 5"; +int tstArab(int n) +{ + return n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : (n % 100 >= 3 && n % 100 <= 10) ? 3 : (n % 100 >= 11 && n % 100 <= 99) || (n % 100 == 1) || (n % 100 ==2) ? 4 : 5; +} + +const wchar_t formGerm[] = L"nplurals=2; plural= n == 1 ? 0 : 1"; +int tstGerm(int n) +{ + return n == 1 ? 0 : 1; +} + +const wchar_t formFren[] = L"nplurals=2; plural= n <= 1 ? 0 : 1"; +int tstFren(int n) +{ + return n <= 1 ? 0 : 1; +} + +const wchar_t formJap[] = L"nplurals=1; plural=0"; +int tstJap(int n) +{ + return 0; +} + +const wchar_t formRom[] = L"nplurals=3; plural= n == 1 ? 0 : n == 0 || (n % 100 >= 1 && n % 100 <= 20) ? 1 : 2 "; +int tstRom(int n) +{ + return n == 1 ? 0 : n == 0 || (n % 100 >= 1 && n % 100 <= 20) ? 1 : 2 ; +} + +const wchar_t formCze[] = L" nplurals=3; plural= n % 100 == 1 ? 0 : n % 100 >= 2 && n % 100 <= 4 ? 1 : 2"; +int tstCze(int n) +{ + return n % 100 == 1 ? 0 : n % 100 >= 2 && n % 100 <= 4 ? 1 : 2; +} + +const wchar_t formSlov[] = L" nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3 "; +int tstSlov(int n) +{ + return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3; +} + +void unitTest() +{ + typedef int (*TestFun)(int); + typedef std::vector<std::pair<const wchar_t*, TestFun> > PhraseFunList; + PhraseFunList phrases; + phrases.push_back(std::make_pair(formPol, &tstPol)); + phrases.push_back(std::make_pair(formRu, &tstRu)); + phrases.push_back(std::make_pair(formLit, &tstLit)); + phrases.push_back(std::make_pair(formArab, &tstArab)); + phrases.push_back(std::make_pair(formGerm, &tstGerm)); + phrases.push_back(std::make_pair(formFren, &tstFren)); + phrases.push_back(std::make_pair(formJap, &tstJap)); + phrases.push_back(std::make_pair(formRom, &tstRom)); + phrases.push_back(std::make_pair(formCze, &tstCze)); + phrases.push_back(std::make_pair(formSlov, &tstSlov)); + + for (PhraseFunList::const_iterator i = phrases.begin(); i != phrases.end(); ++i) + { + PluralForm pf(i->first); + for (int j = 0; j < 10000000; ++j) + assert((i->second)(j) == pf.getForm(j)); + } +} wxString ffs3::translate(const wxString& original) //translate into currently selected language { + /* + int ba = 3; + + unitTest(); + +#ifndef _MSC_VER +#warning 3434 +#endif + + */ + + + //look for translation in buffer table const Translation::const_iterator i = activeTranslation.find(original); if (i != activeTranslation.end()) diff --git a/shared/recycler.cpp b/shared/recycler.cpp index a2b080c9..4fedf01f 100644 --- a/shared/recycler.cpp +++ b/shared/recycler.cpp @@ -146,12 +146,12 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw ( } -void ffs3::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError) +bool ffs3::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError) { #ifdef FFS_WIN const Zstring filenameFmt = applyLongPathPrefix(fileToDelete); if (::GetFileAttributes(filenameFmt.c_str()) == INVALID_FILE_ATTRIBUTES) - return; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! + return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! std::vector<Zstring> fileNames; fileNames.push_back(fileToDelete); @@ -160,7 +160,7 @@ void ffs3::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError) #elif defined FFS_LINUX struct stat fileInfo; if (::lstat(fileToDelete.c_str(), &fileInfo) != 0) - return; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! + return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(fileToDelete.c_str()); //never fails @@ -181,6 +181,7 @@ void ffs3::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError) wxT("(") + errorMessage + wxT(")")); } #endif +return true; } diff --git a/shared/recycler.h b/shared/recycler.h index b9fc9719..404d6f06 100644 --- a/shared/recycler.h +++ b/shared/recycler.h @@ -33,7 +33,7 @@ Linker flag: bool recycleBinExists(); //test existence of Recycle Bin API on current system //move a file or folder to Recycle Bin -void moveToRecycleBin(const Zstring& fileToDelete); //throw (FileError) +bool moveToRecycleBin(const Zstring& fileToDelete); //return "true" if file/dir was actually deleted; throw (FileError) } #endif // RECYCLER_H_INCLUDED |