summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:11:33 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:11:33 +0200
commit6fcfe73ca751f068978ce201094b17cf2dfe4d20 (patch)
treee7d85e8f9057430b480cd0e2f5ccb43c9d2ef8f8 /shared
parent3.15 (diff)
downloadFreeFileSync-6fcfe73ca751f068978ce201094b17cf2dfe4d20.tar.gz
FreeFileSync-6fcfe73ca751f068978ce201094b17cf2dfe4d20.tar.bz2
FreeFileSync-6fcfe73ca751f068978ce201094b17cf2dfe4d20.zip
3.16
Diffstat (limited to 'shared')
-rw-r--r--shared/dir_name.cpp10
-rw-r--r--shared/file_handling.cpp719
-rw-r--r--shared/file_handling.h10
-rw-r--r--shared/i18n.cpp553
-rw-r--r--shared/i18n_no_BOM.cpp553
-rw-r--r--shared/recycler.cpp7
-rw-r--r--shared/recycler.h2
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
bgstack15