summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:11:09 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:11:09 +0200
commit9cc790869ed3905c78c7eeeb0bb44f800b3f2af4 (patch)
tree1c085bbf2302be294866c4fc6e0d225f8abbc346 /shared
parent3.14 (diff)
downloadFreeFileSync-9cc790869ed3905c78c7eeeb0bb44f800b3f2af4.tar.gz
FreeFileSync-9cc790869ed3905c78c7eeeb0bb44f800b3f2af4.tar.bz2
FreeFileSync-9cc790869ed3905c78c7eeeb0bb44f800b3f2af4.zip
3.15
Diffstat (limited to 'shared')
-rw-r--r--shared/ShadowCopy/LockFile.cpp44
-rw-r--r--shared/custom_button.cpp92
-rw-r--r--shared/dir_name.cpp109
-rw-r--r--shared/dir_name.h36
-rw-r--r--shared/dir_picker_i18n.h30
-rw-r--r--shared/dst_hack.cpp84
-rw-r--r--shared/file_error.h14
-rw-r--r--shared/file_handling.cpp1338
-rw-r--r--shared/file_handling.h64
-rw-r--r--shared/file_io.cpp127
-rw-r--r--shared/file_io.h43
-rw-r--r--shared/file_traverser.cpp1
-rw-r--r--shared/i18n.cpp (renamed from shared/localization.cpp)403
-rw-r--r--shared/i18n.h (renamed from shared/localization.h)46
-rw-r--r--shared/i18n_no_BOM.cpp751
-rw-r--r--shared/pch.h119
-rw-r--r--shared/perf.h31
-rw-r--r--shared/privilege.cpp7
-rw-r--r--shared/privilege.h6
-rw-r--r--shared/recycler.cpp4
-rw-r--r--shared/resolve_path.cpp319
-rw-r--r--shared/resolve_path.h19
-rw-r--r--shared/serialize.cpp6
-rw-r--r--shared/serialize.h20
-rw-r--r--shared/shadow.cpp2
-rw-r--r--shared/standard_paths.cpp4
-rw-r--r--shared/string_conv.h18
-rw-r--r--shared/symlink_target.h4
-rw-r--r--shared/system_constants.h3
-rw-r--r--shared/util.cpp23
-rw-r--r--shared/xml_base.cpp14
-rw-r--r--shared/zbase.h38
-rw-r--r--shared/zstring.h6
33 files changed, 2713 insertions, 1112 deletions
diff --git a/shared/ShadowCopy/LockFile.cpp b/shared/ShadowCopy/LockFile.cpp
new file mode 100644
index 00000000..0761a3e3
--- /dev/null
+++ b/shared/ShadowCopy/LockFile.cpp
@@ -0,0 +1,44 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+
+#include <string>
+#include <iostream>
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+
+int wmain(int argc, wchar_t* argv[])
+{
+ if (argc <= 1)
+ {
+ std::wcout << "Please enter the filename to be locked as %1 parameter!" << "\n\n";
+ system("pause");
+ return -1;
+ }
+ std::wstring filename = argv[1];
+
+ //obtain exclusive lock on test file
+ HANDLE hFile = ::CreateFile(filename.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ std::wcout << "Error obtaining exclusive lock on test file: " << filename << "\n\n";
+ system("pause");
+ return -1;
+ }
+
+ std::wcout << "File " << filename << " is locked! Press a key to unlock." << "\n\n";
+ system("pause");
+
+ ::CloseHandle(hFile);
+ return 0;
+}
+
diff --git a/shared/custom_button.cpp b/shared/custom_button.cpp
index de057dc5..df65a401 100644
--- a/shared/custom_button.cpp
+++ b/shared/custom_button.cpp
@@ -8,6 +8,8 @@
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <algorithm>
+#include <limits>
+#include <cmath>
wxButtonWithImage::wxButtonWithImage(wxWindow* parent,
@@ -50,23 +52,29 @@ void wxButtonWithImage::setBitmapBack(const wxBitmap& bitmap, unsigned spaceBefo
}
-void makeWhiteTransparent(const wxColor exceptColor, wxImage& image)
+void makeWhiteTransparent(wxImage& image) //assume black text on white background
{
- unsigned char* alphaData = image.GetAlpha();
- if (alphaData)
+ unsigned char* alphaFirst = image.GetAlpha();
+ if (alphaFirst)
{
- assert(exceptColor.Red() != 255);
+ unsigned char* alphaLast = alphaFirst + image.GetWidth() * image.GetHeight();
- unsigned char exCol = exceptColor.Red(); //alpha value can be extracted from any one of (red/green/blue)
- unsigned char* imageStart = image.GetData();
+ //dist(black, white)
+ double distBlackWhite = std::sqrt(3.0 * 255 * 255);
- unsigned char* j = alphaData;
- const unsigned char* const rowEnd = j + image.GetWidth() * image.GetHeight();
- while (j != rowEnd)
+ const unsigned char* bytePos = image.GetData();
+
+ for (unsigned char* j = alphaFirst; j != alphaLast; ++j)
{
- const unsigned char* imagePixel = imageStart + (j - alphaData) * 3; //each pixel consists of three chars
- //exceptColor(red,green,blue) becomes fully opaque(255), while white(255,255,255) becomes transparent(0)
- *(j++) = ((255 - imagePixel[0]) * wxIMAGE_ALPHA_OPAQUE) / (255 - exCol);
+ unsigned char r = *bytePos++; //
+ unsigned char g = *bytePos++; //each pixel consists of three chars
+ unsigned char b = *bytePos++; //
+
+ //dist((r,g,b), white)
+ double distColWhite = std::sqrt((255.0 - r) * (255.0 - r) + (255.0 - g) * (255.0 - g) + (255.0 - b) * (255.0 - b));
+
+ //black(0,0,0) becomes fully opaque(255), while white(255,255,255) becomes transparent(0)
+ *j = distColWhite / distBlackWhite * wxIMAGE_ALPHA_OPAQUE;
}
}
}
@@ -132,8 +140,11 @@ void linearInterpolation(wxImage& img)
}
*/
+
wxBitmap wxButtonWithImage::createBitmapFromText(const wxString& text)
{
+ //wxDC::DrawLabel() doesn't respect alpha channel at all => apply workaround to calculate alpha values manually:
+
if (text.empty())
return wxBitmap();
@@ -143,29 +154,32 @@ wxBitmap wxButtonWithImage::createBitmapFromText(const wxString& text)
wxSize sizeNeeded = getSizeNeeded(text, currentFont);
wxBitmap newBitmap(sizeNeeded.GetWidth(), sizeNeeded.GetHeight());
- wxMemoryDC dc;
- dc.SelectObject(newBitmap);
-
- //set up white background
- dc.SetBackground(*wxWHITE_BRUSH);
- dc.Clear();
-
- //find position of accelerator
- int indexAccel = -1;
- size_t accelPos;
- wxString textLabelFormatted = text;
- if ((accelPos = text.find(wxT("&"))) != wxString::npos)
{
- textLabelFormatted.Replace(wxT("&"), wxT(""), false); //remove accelerator
- indexAccel = static_cast<int>(accelPos);
- }
+ wxMemoryDC dc;
+ dc.SelectObject(newBitmap);
+
+ //set up white background
+ dc.SetBackground(*wxWHITE_BRUSH);
+ dc.Clear();
+
+ //find position of accelerator
+ int indexAccel = -1;
+ size_t accelPos;
+ wxString textLabelFormatted = text;
+ if ((accelPos = text.find(wxT("&"))) != wxString::npos)
+ {
+ textLabelFormatted.Replace(wxT("&"), wxT(""), false); //remove accelerator
+ indexAccel = static_cast<int>(accelPos);
+ }
+
+ dc.SetTextForeground(*wxBLACK); //for use in makeWhiteTransparent
+ dc.SetTextBackground(*wxWHITE); //
+ dc.SetFont(currentFont);
- dc.SetTextForeground(textColor);
- dc.SetTextBackground(*wxWHITE);
- dc.SetFont(currentFont);
- dc.DrawLabel(textLabelFormatted, wxNullBitmap, wxRect(0, 0, newBitmap.GetWidth(), newBitmap.GetHeight()), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, indexAccel);
+ dc.DrawLabel(textLabelFormatted, wxNullBitmap, wxRect(0, 0, newBitmap.GetWidth(), newBitmap.GetHeight()), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, indexAccel);
- dc.SelectObject(wxNullBitmap);
+ dc.SelectObject(wxNullBitmap);
+ }
//add alpha channel to image
wxImage finalImage(newBitmap.ConvertToImage());
@@ -173,8 +187,18 @@ wxBitmap wxButtonWithImage::createBitmapFromText(const wxString& text)
//linearInterpolation(finalImage);
- //make white background transparent
- makeWhiteTransparent(textColor, finalImage);
+ //calculate values for alpha channel
+ makeWhiteTransparent(finalImage);
+
+ //now apply real text color
+ unsigned char* bytePos = finalImage.GetData();
+ const int pixelCount = finalImage.GetWidth() * finalImage.GetHeight();
+ for (int i = 0; i < pixelCount; ++ i)
+ {
+ *bytePos++ = textColor.Red();
+ *bytePos++ = textColor.Green();
+ *bytePos++ = textColor.Blue();
+ }
return wxBitmap(finalImage);
}
diff --git a/shared/dir_name.cpp b/shared/dir_name.cpp
index 5fd4eab2..3b0c3e82 100644
--- a/shared/dir_name.cpp
+++ b/shared/dir_name.cpp
@@ -8,13 +8,14 @@
#include <wx/dnd.h>
#include <wx/window.h>
#include <wx/textctrl.h>
-#include <wx/filename.h>
+#include <wx/statbox.h>
#include "file_handling.h"
+#include "resolve_path.h"
#include "string_conv.h"
#include "check_exist.h"
#include "util.h"
+#include "i18n.h"
#include "system_constants.h"
-#include <wx/statbox.h>
//define new event type
@@ -25,10 +26,9 @@ typedef void (wxEvtHandler::*FFSFileDropEventFunction)(FFSFileDropEvent&);
#define FFSFileDropEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FFSFileDropEventFunction, &func)
-class FFSFileDropEvent : public wxCommandEvent
+struct FFSFileDropEvent : public wxCommandEvent
{
-public:
- FFSFileDropEvent(const std::vector<wxString>& filesDropped, const wxWindow* dropWindow) :
+ FFSFileDropEvent(const std::vector<wxString>& filesDropped, const wxWindow& dropWindow) :
wxCommandEvent(FFS_DROP_FILE_EVENT),
filesDropped_(filesDropped),
dropWindow_(dropWindow) {}
@@ -39,7 +39,7 @@ public:
}
const std::vector<wxString> filesDropped_;
- const wxWindow* dropWindow_;
+ const wxWindow& dropWindow_;
};
@@ -78,11 +78,12 @@ void setDirectoryName(const wxString& dirname,
wxTextCtrl* txtCtrl,
wxDirPickerCtrl* dirPicker,
wxWindow& tooltipWnd,
+ wxStaticBoxSizer* staticBox,
size_t timeout = 200) //pointers are optional
{
if (txtCtrl)
txtCtrl->ChangeValue(dirname);
- setDirectoryNameImpl(dirname, dirPicker, tooltipWnd, NULL, timeout);
+ setDirectoryNameImpl(dirname, dirPicker, tooltipWnd, staticBox, timeout);
}
@@ -107,8 +108,7 @@ void setDirectoryName(const wxString& dirname,
class WindowDropTarget : public wxFileDropTarget
{
public:
- WindowDropTarget(wxWindow* dropWindow) :
- dropWindow_(dropWindow) {}
+ WindowDropTarget(wxWindow& dropWindow) : dropWindow_(dropWindow) {}
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& fileArray)
{
@@ -120,25 +120,24 @@ public:
{
//create a custom event on drop window: execute event after file dropping is completed! (e.g. after mouse is released)
FFSFileDropEvent evt(filenames, dropWindow_);
- dropWindow_->GetEventHandler()->AddPendingEvent(evt);
+ dropWindow_.GetEventHandler()->AddPendingEvent(evt);
}
return false;
}
private:
- wxWindow* dropWindow_;
+ wxWindow& dropWindow_;
};
//##############################################################################################################
using ffs3::DirectoryNameMainDlg;
-DirectoryNameMainDlg::DirectoryNameMainDlg(
- wxWindow* dropWindow1,
- wxWindow* dropWindow2,
- wxDirPickerCtrl* dirPicker,
- wxComboBox* dirName,
- wxStaticBoxSizer* staticBox) :
+DirectoryNameMainDlg::DirectoryNameMainDlg(wxWindow& dropWindow1,
+ wxWindow& dropWindow2,
+ wxDirPickerCtrl& dirPicker,
+ wxComboBox& dirName,
+ wxStaticBoxSizer& staticBox) :
dropWindow1_(dropWindow1),
dropWindow2_(dropWindow2),
dirPicker_(dirPicker),
@@ -146,16 +145,16 @@ DirectoryNameMainDlg::DirectoryNameMainDlg(
staticBox_(staticBox)
{
//prepare drag & drop
- dropWindow1->SetDropTarget(new WindowDropTarget(dropWindow1)); //takes ownership
- dropWindow2->SetDropTarget(new WindowDropTarget(dropWindow2)); //takes ownership
+ dropWindow1.SetDropTarget(new WindowDropTarget(dropWindow1)); //takes ownership
+ dropWindow2.SetDropTarget(new WindowDropTarget(dropWindow2)); //takes ownership
//redirect drag & drop event back to this class
- dropWindow1->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryNameMainDlg::OnFilesDropped), NULL, this);
- dropWindow2->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryNameMainDlg::OnFilesDropped), NULL, this);
+ dropWindow1.Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryNameMainDlg::OnFilesDropped), NULL, this);
+ dropWindow2.Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryNameMainDlg::OnFilesDropped), NULL, this);
//keep dirPicker and dirName synchronous
- dirName-> Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DirectoryNameMainDlg::OnWriteDirManually), NULL, this );
- dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(DirectoryNameMainDlg::OnDirSelected), NULL, this );
+ dirName .Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DirectoryNameMainDlg::OnWriteDirManually), NULL, this );
+ dirPicker.Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(DirectoryNameMainDlg::OnDirSelected), NULL, this );
}
@@ -164,19 +163,19 @@ void DirectoryNameMainDlg::OnFilesDropped(FFSFileDropEvent& event)
if (event.filesDropped_.empty())
return;
- if (this->dropWindow1_ == event.dropWindow_ || //file may be dropped on window 1 or 2
- this->dropWindow2_ == event.dropWindow_)
+ if (&dropWindow1_ == &event.dropWindow_ || //file may be dropped on window 1 or 2
+ &dropWindow2_ == &event.dropWindow_)
{
if (AcceptDrop(event.filesDropped_))
{
- wxString fileName = event.filesDropped_[0];
- if (wxDirExists(fileName))
- setDirectoryName(fileName, dirName_, dirPicker_, *dirName_, *staticBox_);
+ Zstring fileName = wxToZ(event.filesDropped_[0]);
+ if (dirExists(fileName))
+ setDirectoryName(zToWx(fileName), &dirName_, &dirPicker_, dirName_, staticBox_);
else
{
- fileName = wxFileName(fileName).GetPath();
- if (wxDirExists(fileName))
- setDirectoryName(fileName, dirName_, dirPicker_, *dirName_, *staticBox_);
+ fileName = fileName.BeforeLast(common::FILE_NAME_SEPARATOR);
+ if (dirExists(fileName))
+ setDirectoryName(zToWx(fileName), &dirName_, &dirPicker_, dirName_, staticBox_);
}
}
}
@@ -185,7 +184,7 @@ void DirectoryNameMainDlg::OnFilesDropped(FFSFileDropEvent& event)
void DirectoryNameMainDlg::OnWriteDirManually(wxCommandEvent& event)
{
- setDirectoryName(event.GetString(), NULL, dirPicker_, *dirName_, *staticBox_, 100); //potentially slow network access: wait 100 ms at most
+ setDirectoryName(event.GetString(), NULL, &dirPicker_, dirName_, staticBox_, 100); //potentially slow network access: wait 100 ms at most
event.Skip();
}
@@ -193,7 +192,7 @@ void DirectoryNameMainDlg::OnWriteDirManually(wxCommandEvent& event)
void DirectoryNameMainDlg::OnDirSelected(wxFileDirPickerEvent& event)
{
const wxString newPath = event.GetPath();
- setDirectoryName(newPath, dirName_, NULL, *dirName_, *staticBox_);
+ setDirectoryName(newPath, &dirName_, NULL, dirName_, staticBox_);
event.Skip();
}
@@ -201,35 +200,37 @@ void DirectoryNameMainDlg::OnDirSelected(wxFileDirPickerEvent& event)
Zstring DirectoryNameMainDlg::getName() const
{
- return wxToZ(dirName_->GetValue());
+ return wxToZ(dirName_.GetValue());
}
void DirectoryNameMainDlg::setName(const Zstring& dirname)
{
- setDirectoryName(zToWx(dirname), dirName_, dirPicker_, *dirName_, *staticBox_);
+ setDirectoryName(zToWx(dirname), &dirName_, &dirPicker_, dirName_, staticBox_);
}
//##############################################################################################################
using ffs3::DirectoryName;
-DirectoryName::DirectoryName(wxWindow* dropWindow,
- wxDirPickerCtrl* dirPicker,
- wxTextCtrl* dirName) :
+DirectoryName::DirectoryName(wxWindow& dropWindow,
+ wxDirPickerCtrl& dirPicker,
+ wxTextCtrl& dirName,
+ wxStaticBoxSizer* staticBox) :
dropWindow_(dropWindow),
dirPicker_(dirPicker),
- dirName_(dirName)
+ dirName_(dirName),
+ staticBox_(staticBox)
{
//prepare drag & drop
- dropWindow->SetDropTarget(new WindowDropTarget(dropWindow)); //takes ownership
+ dropWindow.SetDropTarget(new WindowDropTarget(dropWindow)); //takes ownership
//redirect drag & drop event back to this class
- dropWindow->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryName::OnFilesDropped), NULL, this);
+ dropWindow.Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DirectoryName::OnFilesDropped), NULL, this);
//keep dirPicker and dirName synchronous
- dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DirectoryName::OnWriteDirManually ), NULL, this );
- dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DirectoryName::OnDirSelected ), NULL, this );
+ dirName.Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DirectoryName::OnWriteDirManually ), NULL, this );
+ dirPicker.Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DirectoryName::OnDirSelected ), NULL, this );
}
@@ -238,16 +239,16 @@ void DirectoryName::OnFilesDropped(FFSFileDropEvent& event)
if (event.filesDropped_.empty())
return;
- if (this->dropWindow_ == event.dropWindow_)
+ if (&dropWindow_ == &event.dropWindow_)
{
- wxString fileName = event.filesDropped_[0];
- if (wxDirExists(fileName))
- setDirectoryName(fileName, dirName_, dirPicker_, *dirName_);
+ Zstring fileName = wxToZ(event.filesDropped_[0]);
+ if (dirExists(fileName))
+ setDirectoryName(zToWx(fileName), &dirName_, &dirPicker_, dirName_, staticBox_);
else
{
- fileName = wxFileName(fileName).GetPath();
- if (wxDirExists(fileName))
- setDirectoryName(fileName, dirName_, dirPicker_, *dirName_);
+ fileName = fileName.BeforeLast(common::FILE_NAME_SEPARATOR);
+ if (dirExists(fileName))
+ setDirectoryName(zToWx(fileName), &dirName_, &dirPicker_, dirName_, staticBox_);
}
}
}
@@ -255,7 +256,7 @@ void DirectoryName::OnFilesDropped(FFSFileDropEvent& event)
void DirectoryName::OnWriteDirManually(wxCommandEvent& event)
{
- setDirectoryName(event.GetString(), NULL, dirPicker_, *dirName_, 100); //potentially slow network access: wait 100 ms at most
+ setDirectoryName(event.GetString(), NULL, &dirPicker_, dirName_, staticBox_, 100); //potentially slow network access: wait 100 ms at most
event.Skip();
}
@@ -263,7 +264,7 @@ void DirectoryName::OnWriteDirManually(wxCommandEvent& event)
void DirectoryName::OnDirSelected(wxFileDirPickerEvent& event)
{
const wxString newPath = event.GetPath();
- setDirectoryName(newPath, dirName_, NULL, *dirName_);
+ setDirectoryName(newPath, &dirName_, NULL, dirName_, staticBox_);
event.Skip();
}
@@ -271,11 +272,11 @@ void DirectoryName::OnDirSelected(wxFileDirPickerEvent& event)
Zstring DirectoryName::getName() const
{
- return wxToZ(dirName_->GetValue());
+ return wxToZ(dirName_.GetValue());
}
void DirectoryName::setName(const Zstring& dirname)
{
- setDirectoryName(zToWx(dirname), dirName_, dirPicker_, *dirName_);
+ setDirectoryName(zToWx(dirname), &dirName_, &dirPicker_, dirName_, staticBox_);
}
diff --git a/shared/dir_name.h b/shared/dir_name.h
index ad0f2a52..387f5668 100644
--- a/shared/dir_name.h
+++ b/shared/dir_name.h
@@ -15,7 +15,7 @@
#include "zstring.h"
-class FFSFileDropEvent;
+struct FFSFileDropEvent;
class wxCommandEvent;
class wxFileDirPickerEvent;
@@ -26,11 +26,11 @@ namespace ffs3
class DirectoryNameMainDlg : private wxEvtHandler
{
public:
- DirectoryNameMainDlg(wxWindow* dropWindow1,
- wxWindow* dropWindow2,
- wxDirPickerCtrl* dirPicker,
- wxComboBox* dirName,
- wxStaticBoxSizer* staticBox);
+ DirectoryNameMainDlg(wxWindow& dropWindow1,
+ wxWindow& dropWindow2,
+ wxDirPickerCtrl& dirPicker,
+ wxComboBox& dirName,
+ wxStaticBoxSizer& staticBox);
virtual ~DirectoryNameMainDlg() {}
@@ -44,20 +44,21 @@ private:
void OnWriteDirManually(wxCommandEvent& event);
void OnDirSelected(wxFileDirPickerEvent& event);
- const wxWindow* dropWindow1_;
- const wxWindow* dropWindow2_;
- wxDirPickerCtrl* dirPicker_;
- wxComboBox* dirName_;
- wxStaticBoxSizer* staticBox_;
+ const wxWindow& dropWindow1_;
+ const wxWindow& dropWindow2_;
+ wxDirPickerCtrl& dirPicker_;
+ wxComboBox& dirName_;
+ wxStaticBoxSizer& staticBox_;
};
class DirectoryName: private wxEvtHandler
{
public:
- DirectoryName(wxWindow* dropWindow,
- wxDirPickerCtrl* dirPicker,
- wxTextCtrl* dirName);
+ DirectoryName(wxWindow& dropWindow,
+ wxDirPickerCtrl& dirPicker,
+ wxTextCtrl& dirName,
+ wxStaticBoxSizer* staticBox = NULL); //optional
Zstring getName() const;
void setName(const Zstring& dirname);
@@ -67,9 +68,10 @@ private:
void OnWriteDirManually(wxCommandEvent& event);
void OnDirSelected(wxFileDirPickerEvent& event);
- const wxWindow* dropWindow_;
- wxDirPickerCtrl* dirPicker_;
- wxTextCtrl* dirName_;
+ const wxWindow& dropWindow_;
+ wxDirPickerCtrl& dirPicker_;
+ wxTextCtrl& dirName_;
+ wxStaticBoxSizer* staticBox_; //optional
};
}
diff --git a/shared/dir_picker_i18n.h b/shared/dir_picker_i18n.h
new file mode 100644
index 00000000..89b54bf4
--- /dev/null
+++ b/shared/dir_picker_i18n.h
@@ -0,0 +1,30 @@
+#ifndef DIR_PICKER_I18N_H_INCLUDED
+#define DIR_PICKER_I18N_H_INCLUDED
+
+#include <wx/filepicker.h>
+#include "i18n.h"
+
+
+class FfsDirPickerCtrl : public wxDirPickerCtrl
+{
+public:
+ FfsDirPickerCtrl(wxWindow* parent, wxWindowID id,
+ const wxString& path = wxEmptyString,
+ const wxString& message = wxDirSelectorPromptStr,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = wxDIRP_DEFAULT_STYLE,
+ const wxValidator& validator = wxDefaultValidator,
+ const wxString& name = wxDirPickerCtrlNameStr) :
+ wxDirPickerCtrl(parent, id, path, message, pos, size, style, validator, name)
+ {
+#ifdef FFS_WIN
+ //fix wxWidgets localization gap:
+ wxButton* button = dynamic_cast<wxButton*>(m_pickerIface);
+ if (button) button->SetLabel(_("Browse"));
+#endif
+ }
+};
+
+
+#endif // DIR_PICKER_I18N_H_INCLUDED
diff --git a/shared/dst_hack.cpp b/shared/dst_hack.cpp
index a2841ce0..5ec58e06 100644
--- a/shared/dst_hack.cpp
+++ b/shared/dst_hack.cpp
@@ -1,6 +1,6 @@
#include "dst_hack.h"
#include "system_constants.h"
-#include <wx/intl.h>
+#include "i18n.h"
#include "long_path_prefix.h"
#include "string_conv.h"
#include "system_func.h"
@@ -10,51 +10,63 @@
#include "global_func.h"
#include <limits>
+using namespace ffs3;
-bool dst::isFatDrive(const Zstring& fileName) //throw()
-{
- using namespace ffs3;
-
- const size_t BUFFER_SIZE = MAX_PATH + 1;
- wchar_t buffer[BUFFER_SIZE];
+namespace
+{
+//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points)
+Zstring getVolumeName(const Zstring& filename)
+{
//this call is expensive: ~1.5 ms!
- // if (!::GetVolumePathName(applyLongPathPrefix(fileName).c_str(), //__in LPCTSTR lpszFileName,
- // buffer, //__out LPTSTR lpszVolumePathName,
+ // if (!::GetVolumePathName(applyLongPathPrefix(filename).c_str(), //__in LPCTSTR lpszFileName,
+ // fsName, //__out LPTSTR lpszVolumePathName,
// BUFFER_SIZE)) //__in DWORD cchBufferLength
// ...
- // Zstring volumePath = buffer;
+ // Zstring volumePath = fsName;
// if (!volumePath.EndsWith(common::FILE_NAME_SEPARATOR)) //a trailing backslash is required
// volumePath += common::FILE_NAME_SEPARATOR;
- //fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points)
- Zstring volumePath;
- {
- Zstring nameFmt = removeLongPathPrefix(fileName); //throw()
- if (!nameFmt.EndsWith(Zstr("\\")))
- nameFmt += Zstr("\\"); //GetVolumeInformation expects trailing backslash
+ Zstring nameFmt = removeLongPathPrefix(filename); //throw()
+ if (!nameFmt.EndsWith(Zstr("\\")))
+ nameFmt += Zstr("\\"); //GetVolumeInformation expects trailing backslash
- if (nameFmt.StartsWith(Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\"
- {
- size_t nameSize = nameFmt.size();
- const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2);
- if (posFirstSlash != Zstring::npos)
- {
- nameSize = posFirstSlash + 1;
- const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1);
- if (posSecondSlash != Zstring::npos)
- nameSize = posSecondSlash + 1;
- }
- volumePath = Zstring(nameFmt.c_str(), nameSize); //include trailing backslash!
- }
- else //local path: "C:\Folder\"
+ if (nameFmt.StartsWith(Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\"
+ {
+ size_t nameSize = nameFmt.size();
+ const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2);
+ if (posFirstSlash != Zstring::npos)
{
- const size_t pos = nameFmt.find(Zstr(":\\"));
- if (pos != 1) //expect single letter volume
- return false;
- volumePath = Zstring(nameFmt.c_str(), 3);
+ nameSize = posFirstSlash + 1;
+ const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1);
+ if (posSecondSlash != Zstring::npos)
+ nameSize = posSecondSlash + 1;
}
+ return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash!
+ }
+ else //local path: "C:\Folder\"
+ {
+ const size_t pos = nameFmt.find(Zstr(":\\"));
+ if (pos == 1) //expect single letter volume
+ return Zstring(nameFmt.c_str(), 3);
}
+
+ return Zstring();
+}
+}
+
+
+bool dst::isFatDrive(const Zstring& fileName) //throw()
+{
+ using namespace ffs3;
+
+ const size_t BUFFER_SIZE = MAX_PATH + 1;
+ wchar_t fsName[BUFFER_SIZE];
+
+ const Zstring volumePath = getVolumeName(fileName);
+ if (volumePath.empty())
+ return false;
+
//suprisingly fast: ca. 0.03 ms per call!
if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
NULL, //__out LPTSTR lpVolumeNameBuffer,
@@ -62,13 +74,13 @@ bool dst::isFatDrive(const Zstring& fileName) //throw()
NULL, //__out_opt LPDWORD lpVolumeSerialNumber,
NULL, //__out_opt LPDWORD lpMaximumComponentLength,
NULL, //__out_opt LPDWORD lpFileSystemFlags,
- buffer, //__out LPTSTR lpFileSystemNameBuffer,
+ fsName, //__out LPTSTR lpFileSystemNameBuffer,
BUFFER_SIZE)) //__in DWORD nFileSystemNameSize
{
assert(false); //shouldn't happen
return false;
}
- const Zstring fileSystem = buffer;
+ const Zstring fileSystem = fsName;
//DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised)
return fileSystem == Zstr("FAT") ||
diff --git a/shared/file_error.h b/shared/file_error.h
index 152062af..e8497a92 100644
--- a/shared/file_error.h
+++ b/shared/file_error.h
@@ -15,9 +15,7 @@ namespace ffs3
class FileError //Exception base class used to notify file/directory copy/delete errors
{
public:
- FileError(const wxString& message) :
- errorMessage(message) {}
-
+ FileError(const wxString& message) : errorMessage(message) {}
virtual ~FileError() {}
const wxString& msg() const
@@ -29,12 +27,12 @@ private:
const wxString errorMessage;
};
+#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const wxString& message) : FileError(message) {} };
-class ErrorNotExisting : public FileError
-{
-public:
- ErrorNotExisting(const wxString& message) : FileError(message) {}
-};
+DEFINE_NEW_FILE_ERROR(ErrorNotExisting);
+DEFINE_NEW_FILE_ERROR(ErrorTargetExisting);
+DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing);
+DEFINE_NEW_FILE_ERROR(ErrorFileLocked);
}
#endif // FILEERROR_H_INCLUDED
diff --git a/shared/file_handling.cpp b/shared/file_handling.cpp
index 14a4b84c..97c4d077 100644
--- a/shared/file_handling.cpp
+++ b/shared/file_handling.cpp
@@ -5,22 +5,21 @@
// **************************************************************************
//
#include "file_handling.h"
-#include <wx/intl.h>
+#include <map>
+#include <algorithm>
+#include <boost/scoped_array.hpp>
+#include <boost/bind.hpp>
+#include <stdexcept>
#include "system_func.h"
#include "global_func.h"
#include "system_constants.h"
#include "file_traverser.h"
-#include <boost/bind.hpp>
-#include <algorithm>
-#include <wx/datetime.h>
#include "string_conv.h"
-#include <wx/utils.h>
-#include <boost/scoped_array.hpp>
-#include <stdexcept>
#include "loki/TypeManip.h"
#include "loki/ScopeGuard.h"
-#include <map>
#include "symlink_target.h"
+#include "file_io.h"
+#include "i18n.h"
#ifdef FFS_WIN
#include "privilege.h"
@@ -32,7 +31,6 @@
#elif defined FFS_LINUX
#include <sys/stat.h>
-#include "file_io.h"
#include <time.h>
#include <utime.h>
#include <cerrno>
@@ -44,162 +42,7 @@
#endif
using ffs3::FileError;
-
-
-namespace
-{
-#ifdef FFS_WIN
-Zstring resolveRelativePath(const Zstring& relativeName, DWORD proposedBufferSize = 1000)
-{
- boost::scoped_array<Zchar> fullPath(new Zchar[proposedBufferSize]);
- const DWORD rv = ::GetFullPathName(
- relativeName.c_str(), //__in LPCTSTR lpFileName,
- proposedBufferSize, //__in DWORD nBufferLength,
- fullPath.get(), //__out LPTSTR lpBuffer,
- NULL); //__out LPTSTR *lpFilePart
- if (rv == 0 || rv == proposedBufferSize)
- //ERROR! Don't do anything
- return relativeName;
- if (rv > proposedBufferSize)
- return resolveRelativePath(relativeName, rv);
-
- return fullPath.get();
-}
-
-#elif defined FFS_LINUX
-Zstring resolveRelativePath(const Zstring& relativeName) //additional: resolves symbolic links!!!
-{
- char absolutePath[PATH_MAX + 1];
- if (::realpath(relativeName.c_str(), absolutePath) == NULL)
- //ERROR! Don't do anything
- return relativeName;
-
- return Zstring(absolutePath);
-}
-#endif
-
-
-bool replaceMacro(wxString& macro) //macro without %-characters, return true if replaced successfully
-{
- if (macro.IsEmpty())
- return false;
-
- //there are equally named environment variables %TIME%, %DATE% existing, so replace these first!
- if (macro.CmpNoCase(wxT("time")) == 0)
- {
- macro = wxDateTime::Now().FormatISOTime();
- macro.Replace(wxT(":"), wxT(""));
- return true;
- }
-
- if (macro.CmpNoCase(wxT("date")) == 0)
- {
- macro = wxDateTime::Now().FormatISODate();
- return true;
- }
-
- if (macro.CmpNoCase(wxT("month")) == 0)
- {
- macro = wxDateTime::Now().Format(wxT("%B"));
- return true;
- }
-
- if (macro.CmpNoCase(wxT("week")) == 0)
- {
- macro = wxDateTime::Now().Format(wxT("%U"));
- return true;
- }
-
- if (macro.CmpNoCase(wxT("year")) == 0)
- {
- macro = wxDateTime::Now().Format(wxT("%Y"));
- return true;
- }
-
- //try to apply environment variables
- wxString envValue;
- if (wxGetEnv(macro, &envValue))
- {
- macro = envValue;
-
- //some postprocessing:
- macro.Trim(true); //remove leading, trailing blanks
- macro.Trim(false); //
-
- //remove leading, trailing double-quotes
- if (macro.StartsWith(wxT("\"")) &&
- macro.EndsWith(wxT("\"")) &&
- macro.length() >= 2)
- macro = wxString(macro.c_str() + 1, macro.length() - 2);
- return true;
- }
-
- return false;
-}
-
-
-void expandMacros(wxString& text)
-{
- const wxChar SEPARATOR = '%';
-
- if (text.Find(SEPARATOR) != wxNOT_FOUND)
- {
- wxString prefix = text.BeforeFirst(SEPARATOR);
- wxString postfix = text.AfterFirst(SEPARATOR);
- if (postfix.Find(SEPARATOR) != wxNOT_FOUND)
- {
- wxString potentialMacro = postfix.BeforeFirst(SEPARATOR);
- wxString rest = postfix.AfterFirst(SEPARATOR); //text == prefix + SEPARATOR + potentialMacro + SEPARATOR + rest
-
- if (replaceMacro(potentialMacro))
- {
- expandMacros(rest);
- text = prefix + potentialMacro + rest;
- }
- else
- {
- rest = SEPARATOR + rest;
- expandMacros(rest);
- text = prefix + SEPARATOR + potentialMacro + rest;
- }
- }
- }
-}
-}
-
-
-Zstring ffs3::getFormattedDirectoryName(const Zstring& dirname)
-{
- //Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names.
- //note: don't combine directory formatting with wxFileName, as it doesn't respect //?/ - prefix!
-
- wxString dirnameTmp = zToWx(dirname);
- expandMacros(dirnameTmp);
-
- Zstring output = wxToZ(dirnameTmp);
-
- output.Trim();
-
- if (output.empty()) //an empty string will later be returned as "\"; this is not desired
- return Zstring();
-
- /*
- resolve relative names; required by:
- WINDOWS:
- - \\?\-prefix which needs absolute names
- - Volume Shadow Copy: volume name needs to be part of each filename
- - file icon buffer (at least for extensions that are actually read from disk, e.g. "exe")
- - ::SHFileOperation(): Using relative path names is not thread safe
- WINDOWS/LINUX:
- - detection of dependent directories, e.g. "\" and "C:\test"
- */
- output = resolveRelativePath(output);
-
- if (!output.EndsWith(common::FILE_NAME_SEPARATOR))
- output += common::FILE_NAME_SEPARATOR;
-
- return output;
-}
+using namespace ffs3;
bool ffs3::fileExists(const Zstring& filename)
@@ -275,18 +118,22 @@ wxULongLong getFileSizeSymlink(const Zstring& linkName) //throw (FileError)
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
- if (hFile != INVALID_HANDLE_VALUE)
+ if (hFile == INVALID_HANDLE_VALUE)
{
- Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hFile);
- (void)dummy; //silence warning "unused variable"
+ const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + ffs3::zToWx(linkName) + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ }
+ Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hFile);
+ (void)dummy; //silence warning "unused variable"
- BY_HANDLE_FILE_INFORMATION fileInfoByHandle;
- if (::GetFileInformationByHandle(hFile, &fileInfoByHandle))
- return wxULongLong(fileInfoByHandle.nFileSizeHigh, fileInfoByHandle.nFileSizeLow);
+ BY_HANDLE_FILE_INFORMATION fileInfoByHandle = {};
+ if (!::GetFileInformationByHandle(hFile, &fileInfoByHandle))
+ {
+ const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + ffs3::zToWx(linkName) + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
}
- const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + ffs3::zToWx(linkName) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ return wxULongLong(fileInfoByHandle.nFileSizeHigh, fileInfoByHandle.nFileSizeLow);
}
}
#endif
@@ -331,7 +178,7 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
const size_t bufferSize = std::max(pathName.size(), static_cast<size_t>(10000));
boost::scoped_array<wchar_t> buffer(new wchar_t[bufferSize]);
- //pathName need not exist!
+ //full pathName need not yet exist!
if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
buffer.get(), //__out LPTSTR lpszVolumePathName,
static_cast<DWORD>(bufferSize))) //__in DWORD cchBufferLength
@@ -364,8 +211,8 @@ dev_t retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
if (volumePathName.size() > 1 && volumePathName.EndsWith(common::FILE_NAME_SEPARATOR)) //exception: allow '/'
volumePathName = volumePathName.BeforeLast(common::FILE_NAME_SEPARATOR);
- struct stat fileInfo;
- while (::lstat(volumePathName.c_str(), &fileInfo) != 0)
+ struct stat fileInfo = {};
+ while (::lstat(volumePathName.c_str(), &fileInfo) != 0) //go up in folder hierarchy until existing folder is found
{
volumePathName = volumePathName.BeforeLast(common::FILE_NAME_SEPARATOR); //returns empty string if ch not found
if (volumePathName.empty())
@@ -396,48 +243,43 @@ ffs3::ResponseSameVol ffs3::onSameVolume(const Zstring& folderLeft, const Zstrin
void ffs3::removeFile(const Zstring& filename) //throw (FileError);
{
- //no error situation if file is not existing! manual deletion relies on it!
- if (!somethingExists(filename))
- return; //neither file nor any other object (e.g. broken symlink) with that name existing
-
#ifdef FFS_WIN
- const Zstring filenameFmt = applyLongPathPrefix(filename);
-
//remove file, support for \\?\-prefix
+ const Zstring filenameFmt = applyLongPathPrefix(filename);
if (!::DeleteFile(filenameFmt.c_str()))
+#elif defined FFS_LINUX
+ if (::unlink(filename.c_str()) != 0)
+#endif
{
- //optimization: change file attributes ONLY when necessary!
+#ifdef FFS_WIN
+ //perf: apply ONLY when necessary!
if (::GetLastError() == ERROR_ACCESS_DENIED) //function fails if file is read-only
{
- //initialize file attributes
- if (::SetFileAttributes(filenameFmt.c_str(), //address of filename
- FILE_ATTRIBUTE_NORMAL)) //attributes to set
- {
- //now try again...
- if (::DeleteFile(filenameFmt.c_str()))
- return;
- }
+ //(try to) normalize file attributes
+ ::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL);
+
+ //now try again...
+ if (::DeleteFile(filenameFmt.c_str()))
+ return;
}
+#endif
+ //eval error code before next call
+ const wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted();
- wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
-#elif defined FFS_LINUX
- if (::unlink(filename.c_str()) != 0)
- {
- wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ //no error situation if file is not existing! manual deletion relies on it!
+ //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
+
+ throw FileError(errorMessage);
}
-#endif
}
namespace
{
-struct ErrorDifferentVolume : public ffs3::FileError
-{
- ErrorDifferentVolume(const wxString& message) : FileError(message) {}
-};
+DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
/* Usage overview:
@@ -448,7 +290,7 @@ struct ErrorDifferentVolume : public ffs3::FileError
*/
//wrapper for file system rename function:
//throw (FileError); ErrorDifferentVolume if it is due to moving file to another volume
-void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw (FileError, ErrorDifferentVolume)
+void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw (FileError: ErrorDifferentVolume, ErrorTargetExisting)
{
using namespace ffs3; //for zToWx()
@@ -465,8 +307,7 @@ void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw
const DWORD oldNameAttrib = ::GetFileAttributes(oldNameFmt.c_str());
if (oldNameAttrib != INVALID_FILE_ATTRIBUTES)
{
- if (::SetFileAttributes(oldNameFmt.c_str(), //address of filename
- FILE_ATTRIBUTE_NORMAL)) //remove readonly-attribute
+ if (::SetFileAttributes(oldNameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) //remove readonly-attribute
{
//try again...
if (::MoveFileEx(oldNameFmt.c_str(), //__in LPCTSTR lpExistingFileName,
@@ -474,8 +315,7 @@ void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw
0)) //__in DWORD dwFlags
{
//(try to) restore file attributes
- ::SetFileAttributes(newNameFmt.c_str(), //don't handle error
- oldNameAttrib);
+ ::SetFileAttributes(newNameFmt.c_str(), oldNameAttrib); //don't handle error
return;
}
else
@@ -491,22 +331,27 @@ void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw
}
}
+ const DWORD lastError = ::GetLastError();
const wxString errorMessage = wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(oldName) + wxT("\" ->\n\"") + zToWx(newName) + wxT("\"") +
- wxT("\n\n") + ffs3::getLastErrorFormatted();
- if (::GetLastError() == ERROR_NOT_SAME_DEVICE)
+ wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
+ if (lastError == ERROR_NOT_SAME_DEVICE)
throw ErrorDifferentVolume(errorMessage);
+ else if (lastError == ERROR_FILE_EXISTS)
+ throw ErrorTargetExisting(errorMessage);
else
throw FileError(errorMessage);
}
#elif defined FFS_LINUX
- //rename temporary file
if (::rename(oldName.c_str(), newName.c_str()) != 0)
{
+ const int lastError = errno;
const wxString errorMessage = wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(oldName) + wxT("\" ->\n\"") + zToWx(newName) + wxT("\"") +
- wxT("\n\n") + ffs3::getLastErrorFormatted();
- if (errno == EXDEV)
+ wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
+ if (lastError == EXDEV)
throw ErrorDifferentVolume(errorMessage);
+ else if (lastError == EEXIST)
+ throw ErrorTargetExisting(errorMessage);
else
throw FileError(errorMessage);
}
@@ -572,11 +417,11 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw
{
const Zstring fileNameOrig = newName.AfterLast(common::FILE_NAME_SEPARATOR); //returns the whole string if ch not found
const Zstring fileNameShort = getFilenameFmt(newName, ::GetShortPathName).AfterLast(common::FILE_NAME_SEPARATOR); //throw() returns empty string on error
- const Zstring fileNameLong = getFilenameFmt(newName, ::GetLongPathName).AfterLast(common::FILE_NAME_SEPARATOR); //throw() returns empty string on error
+ const Zstring fileNameLong = getFilenameFmt(newName, ::GetLongPathName) .AfterLast(common::FILE_NAME_SEPARATOR); //throw() returns empty string on error
if (!fileNameShort.empty() &&
!fileNameLong.empty() &&
- EqualFilename()(fileNameOrig, fileNameShort) &&
+ EqualFilename() (fileNameOrig, fileNameShort) &&
!EqualFilename()(fileNameShort, fileNameLong))
{
//we detected an event where newName is in shortname format (although it is intended to be a long name) and
@@ -589,7 +434,7 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw
const Zstring parkedTarget = createTemp8Dot3Name(newName);
//move already existing short name out of the way for now
- renameFileInternal(unrelatedPathLong, parkedTarget); //throw (FileError, ErrorDifferentVolume);
+ renameFileInternal(unrelatedPathLong, parkedTarget); //throw (FileError: ErrorDifferentVolume);
//DON'T call ffs3::renameFile() to avoid reentrance!
//schedule cleanup; the file system should assign this unrelated file a new (unique) short name
@@ -607,11 +452,11 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw
//rename file: no copying!!!
-void ffs3::renameFile(const Zstring& oldName, const Zstring& newName) //throw (FileError, ErrorDifferentVolume);
+void ffs3::renameFile(const Zstring& oldName, const Zstring& newName) //throw (FileError: ErrorDifferentVolume, ErrorTargetExisting);
{
try
{
- renameFileInternal(oldName, newName); //throw (FileError, ErrorDifferentVolume)
+ renameFileInternal(oldName, newName); //throw (FileError: ErrorDifferentVolume, ErrorTargetExisting)
}
catch (const FileError&)
{
@@ -624,80 +469,77 @@ void ffs3::renameFile(const Zstring& oldName, const Zstring& newName) //throw (F
}
-using ffs3::MoveFileCallback;
+using ffs3::CallbackMoveFile;
-class CopyCallbackImpl : public ffs3::CopyFileCallback //callback functionality
+class CopyCallbackImpl : public ffs3::CallbackCopyFile //callback functionality
{
public:
- CopyCallbackImpl(const Zstring& sourceFile, MoveFileCallback& callback) : sourceFile_(sourceFile), moveCallback(callback) {}
+ CopyCallbackImpl(const Zstring& sourceFile, CallbackMoveFile& callback) : sourceFile_(sourceFile), moveCallback(callback) {}
+
+ virtual void deleteTargetFile(const Zstring& targetFile) { assert(!fileExists(targetFile)); }
virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred)
{
switch (moveCallback.requestUiRefresh(sourceFile_))
{
- case MoveFileCallback::CONTINUE:
- return CopyFileCallback::CONTINUE;
+ case CallbackMoveFile::CONTINUE:
+ return CallbackCopyFile::CONTINUE;
- case MoveFileCallback::CANCEL:
- return CopyFileCallback::CANCEL;
+ case CallbackMoveFile::CANCEL:
+ return CallbackCopyFile::CANCEL;
}
- return CopyFileCallback::CONTINUE; //dummy return value
+ return CallbackCopyFile::CONTINUE; //dummy return value
}
private:
const Zstring sourceFile_;
- MoveFileCallback& moveCallback;
+ CallbackMoveFile& moveCallback;
};
-void ffs3::moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFileCallback* callback) //throw (FileError);
+void ffs3::moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ignoreExisting, CallbackMoveFile* callback) //throw (FileError);
{
//call back once per file (moveFile() is called by moveDirectory())
if (callback)
switch (callback->requestUiRefresh(sourceFile))
{
- case MoveFileCallback::CONTINUE:
+ case CallbackMoveFile::CONTINUE:
break;
- case MoveFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ case CallbackMoveFile::CANCEL: //a user aborted operation IS an error condition!
throw FileError(wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"") +
wxT("\n\n") + _("Operation aborted!"));
}
- //support case-sensitive renaming
- if (EqualFilename()(sourceFile, targetFile)) //difference in case only
- return renameFile(sourceFile, targetFile); //throw (FileError, ErrorDifferentVolume);
+ const bool targetExisting = fileExists(targetFile);
- if (somethingExists(targetFile)) //test file existence: e.g. Linux might silently overwrite existing symlinks
+ if (targetExisting && !ignoreExisting) //test file existence: e.g. Linux might silently overwrite existing symlinks
throw FileError(wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"") +
wxT("\n\n") + _("Target file already existing!"));
- //moving of symbolic links should work correctly:
-
- //first try to move the file directly without copying
- try
+ if (!targetExisting)
{
- renameFile(sourceFile, targetFile); //throw (FileError, ErrorDifferentVolume);
- return;
- }
- //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file)
- catch (const ErrorDifferentVolume&) {}
-
+ //try to move the file directly without copying
+ try
+ {
+ renameFile(sourceFile, targetFile); //throw (FileError: ErrorDifferentVolume);
+ return; //great, we get away cheaply!
+ }
+ //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file)
+ catch (const ErrorDifferentVolume&) {}
- //file is on a different volume: let's copy it
- std::auto_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(sourceFile, *callback) : NULL);
+ //file is on a different volume: let's copy it
+ std::auto_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(sourceFile, *callback) : NULL);
- copyFile(sourceFile,
- targetFile,
- true, //copy symbolic links
- false, //dont copy filesystem permissions
-#ifdef FFS_WIN
- NULL, //supply handler for making shadow copies
-#endif
- copyCallback.get()); //throw (FileError);
+ if (symlinkExists(sourceFile))
+ copySymlink(sourceFile, targetFile, SYMLINK_TYPE_FILE, false); //throw (FileError) dont copy filesystem permissions
+ else
+ copyFile(sourceFile, targetFile, false, copyCallback.get()); //throw (FileError);
- //attention: if copy-operation was cancelled an exception is thrown => sourcefile is not deleted, as we wish!
+ //attention: if copy-operation was cancelled an exception is thrown => sourcefile is not deleted, as we wish!
+ }
- removeFile(sourceFile);
+ removeFile(sourceFile); //throw (FileError)
+ //note: copying file is NOT undone in case of exception: currently this function is called in context of user-defined deletion dir, where this behavior is fine
}
namespace
@@ -705,45 +547,48 @@ namespace
class TraverseOneLevel : public ffs3::TraverseCallback
{
public:
- typedef std::vector<std::pair<Zstring, Zstring> > NamePair;
+ typedef std::pair<Zstring, Zstring> NamePair;
+ typedef std::vector<NamePair> NameList;
- TraverseOneLevel(NamePair& filesShort, NamePair& dirsShort) :
- m_files(filesShort),
- m_dirs(dirsShort) {}
+ TraverseOneLevel(NameList& files, NameList& dirs) :
+ files_(files),
+ dirs_(dirs) {}
virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
{
- m_files.push_back(std::make_pair(Zstring(shortName), fullName));
+ files_.push_back(NamePair(shortName, fullName));
}
+
virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
{
if (details.dirLink)
- m_dirs.push_back(std::make_pair(Zstring(shortName), fullName));
+ dirs_.push_back(NamePair(shortName, fullName));
else
- m_files.push_back(std::make_pair(Zstring(shortName), fullName));
+ files_.push_back(NamePair(shortName, fullName));
}
virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName)
{
- m_dirs.push_back(std::make_pair(Zstring(shortName), fullName));
+ dirs_.push_back(NamePair(shortName, fullName));
return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs; moveDirectory works recursively!
}
+
virtual void onError(const wxString& errorText)
{
throw FileError(errorText);
}
private:
- NamePair& m_files;
- NamePair& m_dirs;
+ NameList& files_;
+ NameList& dirs_;
};
-struct RemoveCallbackImpl : public ffs3::RemoveDirCallback
+struct RemoveCallbackImpl : public ffs3::CallbackRemoveDir
{
RemoveCallbackImpl(const Zstring& sourceDir,
const Zstring& targetDir,
- MoveFileCallback& moveCallback) :
+ CallbackMoveFile& moveCallback) :
sourceDir_(sourceDir),
targetDir_(targetDir),
moveCallback_(moveCallback) {}
@@ -752,9 +597,9 @@ struct RemoveCallbackImpl : public ffs3::RemoveDirCallback
{
switch (moveCallback_.requestUiRefresh(sourceDir_))
{
- case MoveFileCallback::CONTINUE:
+ case CallbackMoveFile::CONTINUE:
break;
- case MoveFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ case CallbackMoveFile::CANCEL: //a user aborted operation IS an error condition!
throw ffs3::FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + ffs3::zToWx(sourceDir_) + wxT("\" ->\n\"") +
ffs3::zToWx(targetDir_) + wxT("\"") + wxT("\n\n") + _("Operation aborted!"));
}
@@ -763,12 +608,12 @@ struct RemoveCallbackImpl : public ffs3::RemoveDirCallback
private:
const Zstring sourceDir_;
const Zstring targetDir_;
- MoveFileCallback& moveCallback_;
+ CallbackMoveFile& moveCallback_;
};
}
-void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExistingDirs, MoveFileCallback* callback) //throw (FileError);
+void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw (FileError);
{
using namespace ffs3;
@@ -776,63 +621,63 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
if (callback)
switch (callback->requestUiRefresh(sourceDir))
{
- case MoveFileCallback::CONTINUE:
+ case CallbackMoveFile::CONTINUE:
break;
- case MoveFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ case CallbackMoveFile::CANCEL: //a user aborted operation IS an error condition!
throw FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") +
zToWx(targetDir) + wxT("\"") + wxT("\n\n") + _("Operation aborted!"));
}
- //handle symbolic links
- if (symlinkExists(sourceDir))
- {
- createDirectory(targetDir, sourceDir, true, false); //copy symbolic link, don't copy permissions
- removeDirectory(sourceDir, NULL); //if target is already another symlink or directory, sourceDir-symlink is silently deleted
- return;
- }
+ const bool targetExisting = dirExists(targetDir);
- if (somethingExists(targetDir))
- {
- if (!ignoreExistingDirs) //directory or symlink exists (or even a file... this error will be caught later)
- throw FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") + zToWx(targetDir) + wxT("\"") +
- wxT("\n\n") + _("Target directory already existing!"));
- }
- else
+ if (targetExisting && !ignoreExisting) //directory or symlink exists (or even a file... this error will be caught later)
+ throw FileError(wxString(_("Error moving directory:")) + wxT("\n\"") + zToWx(sourceDir) + wxT("\" ->\n\"") + zToWx(targetDir) + wxT("\"") +
+ wxT("\n\n") + _("Target directory already existing!"));
+
+ const bool isSymlink = symlinkExists(sourceDir);
+
+ if (!targetExisting)
{
//first try to move the directory directly without copying
try
{
- renameFile(sourceDir, targetDir); //throw (FileError, ErrorDifferentVolume);
- return;
+ renameFile(sourceDir, targetDir); //throw (FileError: ErrorDifferentVolume, ErrorTargetExisting);
+ return; //great, we get away cheaply!
}
//if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the directory)
catch (const ErrorDifferentVolume&) {}
//create target
- createDirectory(targetDir, sourceDir, false, false); //throw (FileError); don't copy permissions
+ if (isSymlink)
+ copySymlink(sourceDir, targetDir, SYMLINK_TYPE_DIR, false); //throw (FileError) -> don't copy permissions
+ else
+ createDirectory(targetDir, sourceDir, false); //throw (FileError)
}
- //move files/folders recursively
- TraverseOneLevel::NamePair fileList; //list of names: 1. short 2.long
- TraverseOneLevel::NamePair dirList; //
+ if (!isSymlink) //handle symbolic links
+ {
+ //move files/folders recursively
+ TraverseOneLevel::NameList fileList; //list of names: 1. short 2.long
+ TraverseOneLevel::NameList dirList; //
- //traverse source directory one level
- TraverseOneLevel traverseCallback(fileList, dirList);
- traverseFolder(sourceDir, false, traverseCallback); //traverse one level, don't follow symlinks
+ //traverse source directory one level
+ TraverseOneLevel traverseCallback(fileList, dirList);
+ traverseFolder(sourceDir, false, traverseCallback); //traverse one level, don't follow symlinks
- const Zstring targetDirFormatted = targetDir.EndsWith(common::FILE_NAME_SEPARATOR) ? //ends with path separator
- targetDir :
- targetDir + common::FILE_NAME_SEPARATOR;
+ const Zstring targetDirFormatted = targetDir.EndsWith(common::FILE_NAME_SEPARATOR) ? //ends with path separator
+ targetDir :
+ targetDir + common::FILE_NAME_SEPARATOR;
- //move files
- for (TraverseOneLevel::NamePair::const_iterator i = fileList.begin(); i != fileList.end(); ++i)
- ffs3::moveFile(i->second, targetDirFormatted + i->first, callback);
+ //move files
+ for (TraverseOneLevel::NameList::const_iterator i = fileList.begin(); i != fileList.end(); ++i)
+ ffs3::moveFile(i->second, targetDirFormatted + i->first, ignoreExisting, callback); //throw (FileError: ErrorTargetExisting);
- //move directories
- for (TraverseOneLevel::NamePair::const_iterator i = dirList.begin(); i != dirList.end(); ++i)
- ::moveDirectoryImpl(i->second, targetDirFormatted + i->first, true, callback);
+ //move directories
+ for (TraverseOneLevel::NameList::const_iterator i = dirList.begin(); i != dirList.end(); ++i)
+ ::moveDirectoryImpl(i->second, targetDirFormatted + i->first, ignoreExisting, callback);
- //attention: if move-operation was cancelled an exception is thrown => sourceDir is not deleted, as we wish!
+ //attention: if move-operation was cancelled an exception is thrown => sourceDir is not deleted, as we wish!
+ }
//delete source
std::auto_ptr<RemoveCallbackImpl> removeCallback(callback != NULL ? new RemoveCallbackImpl(sourceDir, targetDir, *callback) : NULL);
@@ -840,7 +685,7 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
}
-void ffs3::moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExistingDirs, MoveFileCallback* callback) //throw (FileError);
+void ffs3::moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw (FileError);
{
#ifdef FFS_WIN
const Zstring& sourceDirFormatted = sourceDir;
@@ -857,7 +702,7 @@ void ffs3::moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, boo
targetDir;
#endif
- ::moveDirectoryImpl(sourceDirFormatted, targetDirFormatted, ignoreExistingDirs, callback);
+ ::moveDirectoryImpl(sourceDirFormatted, targetDirFormatted, ignoreExisting, callback);
}
@@ -895,7 +740,7 @@ private:
};
-void ffs3::removeDirectory(const Zstring& directory, RemoveDirCallback* callback)
+void ffs3::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback)
{
//no error situation if directory is not existing! manual deletion relies on it!
if (!somethingExists(directory))
@@ -904,17 +749,10 @@ void ffs3::removeDirectory(const Zstring& directory, RemoveDirCallback* callback
#ifdef FFS_WIN
const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix
- //initialize file attributes
- if (!::SetFileAttributes( // initialize file attributes: actually NEEDED for symbolic links also!
- directoryFmt.c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL)) // attributes to set
- {
- wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
+ //(try to) normalize file attributes: actually NEEDED for symbolic links also!
+ ::SetFileAttributes(directoryFmt.c_str(), FILE_ATTRIBUTE_NORMAL);
#endif
-
//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
if (symlinkExists(directory)) //remove symlink directly
{
@@ -970,7 +808,6 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
{
#ifdef FFS_WIN
FILETIME creationTime = {};
- FILETIME lastAccessTime = {};
FILETIME lastWriteTime = {};
{
@@ -1006,7 +843,7 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
if (!::GetFileTime(hSource, //__in HANDLE hFile,
&creationTime, //__out_opt LPFILETIME lpCreationTime,
- &lastAccessTime, //__out_opt LPFILETIME lpLastAccessTime,
+ NULL, //__out_opt LPFILETIME lpLastAccessTime,
&lastWriteTime)) //__out_opt LPFILETIME lpLastWriteTime
{
const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceObj) + wxT("\"");
@@ -1016,7 +853,6 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
else
{
creationTime = sourceAttr.ftCreationTime;
- lastAccessTime = sourceAttr.ftLastAccessTime;
lastWriteTime = sourceAttr.ftLastWriteTime;
}
@@ -1065,7 +901,7 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
if (!::SetFileTime(hTarget,
&creationTime,
- &lastAccessTime,
+ NULL,
&lastWriteTime))
{
wxString errorMessage = wxString(_("Error changing modification time:")) + wxT("\n\"") + zToWx(targetObj) + wxT("\"");
@@ -1088,14 +924,14 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
#elif defined FFS_LINUX
if (deRefSymlinks)
{
- struct stat objInfo;
+ struct stat objInfo = {};
if (::stat(sourceObj.c_str(), &objInfo) != 0) //read file attributes from source directory
{
const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceObj) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
}
- struct utimbuf newTimes;
+ struct utimbuf newTimes = {};
newTimes.actime = objInfo.st_atime;
newTimes.modtime = objInfo.st_mtime;
@@ -1108,14 +944,14 @@ void ffs3::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, boo
}
else
{
- struct stat objInfo;
+ struct stat objInfo = {};
if (::lstat(sourceObj.c_str(), &objInfo) != 0) //read file attributes from source directory
{
const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + zToWx(sourceObj) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
}
- struct timeval newTimes[2];
+ struct timeval newTimes[2] = {};
newTimes[0].tv_sec = objInfo.st_atime; /* seconds */
newTimes[0].tv_usec = 0; /* microseconds */
@@ -1207,75 +1043,10 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa
}
#endif
-enum SymlinkType
-{
- SYMLINK_TYPE_FILE,
- SYMLINK_TYPE_DIR
-};
-void copySymlinkInternal(const Zstring& sourceLink, const Zstring& targetLink, SymlinkType type, bool copyFilePermissions) //throw (FileError)
-{
- using namespace ffs3;
-
-#ifdef FFS_WIN
- const Zstring linkPath = getSymlinkRawTargetString(sourceLink); //accept broken symlinks; throw (FileError)
-
- //dynamically load windows API function
- typedef BOOLEAN (WINAPI *CreateSymbolicLinkFunc)(
- LPCTSTR lpSymlinkFileName,
- LPCTSTR lpTargetFileName,
- DWORD dwFlags);
- static const CreateSymbolicLinkFunc createSymbolicLink = util::getDllFun<CreateSymbolicLinkFunc>(L"kernel32.dll", "CreateSymbolicLinkW");
- if (createSymbolicLink == NULL)
- throw FileError(wxString(_("Error loading library function:")) + wxT("\n\"") + wxT("CreateSymbolicLinkW") + wxT("\""));
-
- if (!createSymbolicLink( //seems no long path prefix is required...
- targetLink.c_str(), //__in LPTSTR lpSymlinkFileName,
- linkPath.c_str(), //__in LPTSTR lpTargetFileName,
- (type == SYMLINK_TYPE_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags
- {
- const wxString errorMessage = wxString(_("Error copying symbolic link:")) + wxT("\n\"") + zToWx(sourceLink) + wxT("\" ->\n\"") + zToWx(targetLink) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
-
-#elif defined FFS_LINUX
- //copy symbolic link
- const int BUFFER_SIZE = 10000;
- char buffer[BUFFER_SIZE];
- const int bytesWritten = ::readlink(sourceLink.c_str(), buffer, BUFFER_SIZE);
- if (bytesWritten < 0 || bytesWritten == BUFFER_SIZE)
- {
- wxString errorMessage = wxString(_("Error resolving symbolic link:")) + wxT("\n\"") + zToWx(sourceLink) + wxT("\"");
- if (bytesWritten < 0) errorMessage += wxString(wxT("\n\n")) + ffs3::getLastErrorFormatted();
- throw FileError(errorMessage);
- }
- //set null-terminating char
- buffer[bytesWritten] = 0;
-
- if (::symlink(buffer, targetLink.c_str()) != 0)
- {
- const wxString errorMessage = wxString(_("Error copying symbolic link:")) + wxT("\n\"") + zToWx(sourceLink) + wxT("\" ->\n\"") + zToWx(targetLink) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
-#endif
-
- //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist
- Loki::ScopeGuard guardNewDir = type == SYMLINK_TYPE_DIR ?
- Loki::MakeGuard(&TryCleanUp::tryDeleteDir, targetLink) :
- Loki::MakeGuard(&TryCleanUp::tryDeleteFile, targetLink);
-
- copyFileTimes(sourceLink, targetLink, false); //throw (FileError)
-
- if (copyFilePermissions)
- copyObjectPermissions(sourceLink, targetLink, false); //throw FileError()
-
- guardNewDir.Dismiss(); //target has been created successfully!
-}
-}
-
#ifdef HAVE_SELINUX
//copy SELinux security context
-void copySecurityContext(const Zstring& source, const Zstring& target, bool derefSymlinks) //throw FileError()
+void copySecurityContext(const Zstring& source, const Zstring& target, bool derefSymlinks) //throw (FileError)
{
using ffs3::zToWx;
@@ -1329,7 +1100,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, bool dere
//copy permissions for files, directories or symbolic links: requires admin rights
-void ffs3::copyObjectPermissions(const Zstring& source, const Zstring& target, bool derefSymlinks) //throw FileError();
+void copyObjectPermissions(const Zstring& source, const Zstring& target, bool derefSymlinks) //throw (FileError);
{
#ifdef FFS_WIN
//setting privileges requires admin rights!
@@ -1431,13 +1202,13 @@ void ffs3::copyObjectPermissions(const Zstring& source, const Zstring& target, b
#elif defined FFS_LINUX
#ifdef HAVE_SELINUX //copy SELinux security context
- copySecurityContext(source, target, derefSymlinks); //throw FileError()
+ copySecurityContext(source, target, derefSymlinks); //throw (FileError)
#endif
if (derefSymlinks)
{
struct stat fileInfo;
- if (::stat(source.c_str(), &fileInfo) != 0 ||
+ if (::stat (source.c_str(), &fileInfo) != 0 ||
::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights!
::chmod(target.c_str(), fileInfo.st_mode) != 0)
{
@@ -1448,7 +1219,7 @@ void ffs3::copyObjectPermissions(const Zstring& source, const Zstring& target, b
else
{
struct stat fileInfo;
- if (::lstat(source.c_str(), &fileInfo) != 0 ||
+ if (::lstat (source.c_str(), &fileInfo) != 0 ||
::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights!
(!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0)) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
{
@@ -1460,7 +1231,7 @@ void ffs3::copyObjectPermissions(const Zstring& source, const Zstring& target, b
}
-void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyDirectorySymLinks, bool copyFilePermissions, int level)
+void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions, int level)
{
using namespace ffs3;
@@ -1476,108 +1247,110 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
{
//call function recursively
const Zstring templateParent = templateDir.BeforeLast(common::FILE_NAME_SEPARATOR);
- createDirectoryRecursively(dirParent, templateParent, false, copyFilePermissions, level + 1); //don't create symbolic links in recursion!
+ createDirectoryRecursively(dirParent, templateParent, copyFilePermissions, level + 1);
}
//now creation should be possible
- if (templateDir.empty() || !somethingExists(templateDir))
- {
- //default directory creation
+ //default directory creation
#ifdef FFS_WIN
- if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), NULL))// pointer to a directory path string
+ //don't use ::CreateDirectoryEx:
+ //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage
+ //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)!
+ //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!)
+ if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), NULL))
#elif defined FFS_LINUX
- if (::mkdir(directory.c_str(), 0755) != 0)
+ if (::mkdir(directory.c_str(), 0755) != 0)
#endif
- {
- if (level != 0) return;
- wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
+ {
+ if (level != 0) return;
+ wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
}
- else
+
+ if (!templateDir.empty())
{
#ifdef FFS_WIN
- //symbolic link handling
- if (symlinkExists(templateDir))
+ //try to copy file attributes
+ Zstring sourcePath;
+
+ if (symlinkExists(templateDir)) //dereference symlink!
{
- if (copyDirectorySymLinks)
- return copySymlinkInternal(templateDir, directory, SYMLINK_TYPE_DIR, copyFilePermissions); //throw (FileError)
- else //create directory based on target of symbolic link
+ try
{
//get target directory of symbolic link
- Zstring linkPath;
- try
- {
- linkPath = resolveDirectorySymlink(templateDir); //throw (FileError)
- }
- catch (FileError&)
- {
- //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error...
- if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), // pointer to a directory path string
- NULL))
- {
- if (level != 0) return;
- const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
- return;
- }
-
- if (!::CreateDirectoryEx( // this function automatically copies symbolic links if encountered
- applyLongPathPrefix(linkPath).c_str(), // pointer to path string of template directory
- applyLongPathPrefixCreateDir(directory).c_str(), // pointer to a directory path string
- NULL))
- {
- if (level != 0) return;
- const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
- }
+ sourcePath = resolveDirectorySymlink(templateDir); //throw (FileError)
}
+ catch (FileError&) {} //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error...
}
else //no symbolic link
+ sourcePath = templateDir;
+
+ //try to copy file attributes
+ if (!sourcePath.empty())
{
- //automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)!
- //also it isn't able to copy most junctions because of missing permissions (although target path can be retrieved!)
- if (!::CreateDirectoryEx(
- applyLongPathPrefix(templateDir).c_str(), // pointer to path string of template directory
- applyLongPathPrefixCreateDir(directory).c_str(), // pointer to a directory path string
- NULL))
+ const DWORD sourceAttr = ::GetFileAttributes(applyLongPathPrefix(sourcePath).c_str());
+ if (sourceAttr != INVALID_FILE_ATTRIBUTES)
{
- if (level != 0) return;
- const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ const bool isCompressed = (sourceAttr & FILE_ATTRIBUTE_COMPRESSED) != 0;
+ const bool isEncrypted = (sourceAttr & FILE_ATTRIBUTE_ENCRYPTED) != 0;
+
+ ::SetFileAttributes(applyLongPathPrefix(directory), sourceAttr);
+
+ if (isEncrypted)
+ ::EncryptFile(directory.c_str()); //seems no long path is required (check passed!)
+
+ if (isCompressed)
+ {
+ HANDLE hDir = ::CreateFile(applyLongPathPrefix(directory).c_str(),
+ GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hDir);
+ (void)dummy; //silence warning "unused variable"
+
+ USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
+
+ DWORD bytesReturned = 0;
+ ::DeviceIoControl(hDir, //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
+ }
+ }
}
}
+#endif
-#elif defined FFS_LINUX
- //symbolic link handling
- if (copyDirectorySymLinks &&
- symlinkExists(templateDir))
- //there is no directory-type symlink in Linux! => just copy as file
- return copySymlinkInternal(templateDir, directory, SYMLINK_TYPE_DIR, copyFilePermissions); //throw (FileError)
-
- //default directory creation
- if (::mkdir(directory.c_str(), 0755) != 0)
+ //try to copy file times: NOT mission critical for a directory
+ try
{
- if (level != 0) return;
- wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ copyFileTimes(templateDir, directory, true); //throw (FileError)
}
-#endif
- Loki::ScopeGuard guardNewDir = Loki::MakeGuard(&TryCleanUp::tryDeleteDir, directory); //ensure cleanup:
+ catch (FileError&) {}
- copyFileTimes(templateDir, directory, !copyDirectorySymLinks); //throw (FileError)
+ Loki::ScopeGuard guardNewDir = Loki::MakeGuard(&TryCleanUp::tryDeleteDir, directory); //ensure cleanup:
+ //enforce copying file permissions: it's advertized on GUI...
if (copyFilePermissions)
- copyObjectPermissions(templateDir, directory, !copyDirectorySymLinks); //throw FileError()
+ copyObjectPermissions(templateDir, directory, true); //throw (FileError)
guardNewDir.Dismiss(); //target has been created successfully!
}
}
+}
-void ffs3::createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyDirectorySymLinks, bool copyFilePermissions)
+void ffs3::createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions)
{
//remove trailing separator
const Zstring dirFormatted = directory.EndsWith(common::FILE_NAME_SEPARATOR) ?
@@ -1588,13 +1361,56 @@ void ffs3::createDirectory(const Zstring& directory, const Zstring& templateDir,
templateDir.BeforeLast(common::FILE_NAME_SEPARATOR) :
templateDir;
- createDirectoryRecursively(dirFormatted, templateFormatted, copyDirectorySymLinks, copyFilePermissions, 0);
+ createDirectoryRecursively(dirFormatted, templateFormatted, copyFilePermissions, 0);
}
void ffs3::createDirectory(const Zstring& directory)
{
- ffs3::createDirectory(directory, Zstring(), false, false);
+ ffs3::createDirectory(directory, Zstring(), false);
+}
+
+
+void ffs3::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, ffs3::SymlinkType type, bool copyFilePermissions) //throw (FileError)
+{
+ const Zstring linkPath = getSymlinkRawTargetString(sourceLink); //accept broken symlinks; throw (FileError)
+
+#ifdef FFS_WIN
+ //dynamically load windows API function
+ typedef BOOLEAN (WINAPI *CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags);
+ static const CreateSymbolicLinkFunc createSymbolicLink = util::getDllFun<CreateSymbolicLinkFunc>(L"kernel32.dll", "CreateSymbolicLinkW");
+ if (createSymbolicLink == NULL)
+ throw FileError(wxString(_("Error loading library function:")) + wxT("\n\"") + wxT("CreateSymbolicLinkW") + wxT("\""));
+
+ if (!createSymbolicLink( //seems no long path prefix is required...
+ targetLink.c_str(), //__in LPTSTR lpSymlinkFileName,
+ linkPath.c_str(), //__in LPTSTR lpTargetFileName,
+ (type == SYMLINK_TYPE_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags
+ {
+ const wxString errorMessage = wxString(_("Error copying symbolic link:")) + wxT("\n\"") + zToWx(sourceLink) + wxT("\" ->\n\"") + zToWx(targetLink) + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ }
+
+#elif defined FFS_LINUX
+ if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
+ {
+ const wxString errorMessage = wxString(_("Error copying symbolic link:")) + wxT("\n\"") + zToWx(sourceLink) + wxT("\" ->\n\"") + zToWx(targetLink) + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + ffs3::getLastErrorFormatted());
+ }
+#endif
+
+ //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist
+ Loki::ScopeGuard guardNewDir = type == SYMLINK_TYPE_DIR ?
+ Loki::MakeGuard(&TryCleanUp::tryDeleteDir, targetLink) :
+ Loki::MakeGuard(&TryCleanUp::tryDeleteFile, targetLink);
+
+ //file times: essential for a symlink: enforce this! (don't just try!)
+ copyFileTimes(sourceLink, targetLink, false); //throw (FileError)
+
+ if (copyFilePermissions)
+ copyObjectPermissions(sourceLink, targetLink, false); //throw (FileError)
+
+ guardNewDir.Dismiss(); //target has been created successfully!
}
@@ -1610,15 +1426,8 @@ Zstring createTempName(const Zstring& filename)
return output;
}
-}
#ifdef FFS_WIN
-namespace
-{
-#ifndef COPY_FILE_COPY_SYMLINK
-#define COPY_FILE_COPY_SYMLINK 0x00000800
-#endif
-
DWORD CALLBACK copyCallbackInternal(
LARGE_INTEGER totalFileSize,
LARGE_INTEGER totalBytesTransferred,
@@ -1630,11 +1439,11 @@ DWORD CALLBACK copyCallbackInternal(
HANDLE hDestinationFile,
LPVOID lpData)
{
- using ffs3::CopyFileCallback;
+ using ffs3::CallbackCopyFile;
//small performance optimization: it seems this callback function is called for every 64 kB (depending on cluster size).
static size_t callNr = 0;
- if (++callNr % 4 == 0) //executing callback for each 256 kB should suffice
+ if (++callNr % 4 == 0) //executing callback every 256 kB should suffice
{
if (lpData != NULL)
{
@@ -1646,14 +1455,14 @@ DWORD CALLBACK copyCallbackInternal(
This will then be handled in future versions of FreeFileSync.\n\nThanks -ZenJu"),
NULL, 0);
- CopyFileCallback* callback = static_cast<CopyFileCallback*>(lpData);
+ CallbackCopyFile* callback = static_cast<CallbackCopyFile*>(lpData);
try
{
switch (callback->updateCopyStatus(wxULongLong(totalBytesTransferred.HighPart, totalBytesTransferred.LowPart)))
{
- case CopyFileCallback::CONTINUE:
+ case CallbackCopyFile::CONTINUE:
break;
- case CopyFileCallback::CANCEL:
+ case CallbackCopyFile::CANCEL:
return PROGRESS_CANCEL;
}
}
@@ -1668,6 +1477,10 @@ DWORD CALLBACK copyCallbackInternal(
}
+#ifndef COPY_FILE_COPY_SYMLINK
+#define COPY_FILE_COPY_SYMLINK 0x00000800
+#endif
+
bool supportForSymbolicLinks()
{
OSVERSIONINFO osvi = {};
@@ -1685,11 +1498,9 @@ bool supportForSymbolicLinks()
#define COPY_FILE_ALLOW_DECRYPTED_DESTINATION 0x00000008
#endif
-
bool supportForNonEncryptedDestination()
{
- OSVERSIONINFO osvi;
- ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
//encrypted destination is not supported with Windows 2000
@@ -1699,39 +1510,28 @@ bool supportForNonEncryptedDestination()
//overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
return false;
}
-}
-void ffs3::copyFile(const Zstring& sourceFile,
- const Zstring& targetFile,
- bool copyFileSymLinks,
- bool copyFilePermissions,
- shadow::ShadowCopy* shadowCopyHandler,
- ffs3::CopyFileCallback* callback)
+void rawCopyWinApi(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ CallbackCopyFile* callback) //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked)
{
- //ffs3::fileExists(targetFile) -> avoid this call, performance;
- //if target exists (very unlikely, because sync-algorithm deletes it) renaming below will fail!
+ Loki::ScopeGuard guardTarget = Loki::MakeGuard(&removeFile, targetFile); //transactional behavior: guard just before starting copy!
DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
- //copy symbolic links instead of the files pointed at
- static const bool symlinksSupported = supportForSymbolicLinks(); //only set "true" if supported by OS: else copying in Windows XP fails
- if (copyFileSymLinks && symlinksSupported)
- copyFlags |= COPY_FILE_COPY_SYMLINK;
+ // static const bool symlinksSupported = supportForSymbolicLinks(); //only set "true" if supported by OS: else copying in Windows XP fails
+ // if (copyFileSymLinks && symlinksSupported)
+ // copyFlags |= COPY_FILE_COPY_SYMLINK;
//allow copying from encrypted to non-encrytped location
static const bool nonEncSupported = supportForNonEncryptedDestination();
if (nonEncSupported)
copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
- const Zstring temporary = createTempName(targetFile); //use temporary file until a correct date has been set
-
- Loki::ScopeGuard guardTempFile = Loki::MakeGuard(&ffs3::removeFile, temporary);
-
-
- if (!::CopyFileEx( //same performance as CopyFile()
+ if (!::CopyFileEx( //same performance like CopyFile()
applyLongPathPrefix(sourceFile).c_str(),
- applyLongPathPrefix(temporary).c_str(),
+ applyLongPathPrefix(targetFile).c_str(),
callback != NULL ? copyCallbackInternal : NULL,
callback,
NULL,
@@ -1741,44 +1541,30 @@ void ffs3::copyFile(const Zstring& sourceFile,
//don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition!
- //if file is locked (try to) use Windows Volume Shadow Copy Service
- if (shadowCopyHandler != NULL &&
- (lastError == ERROR_SHARING_VIOLATION ||
- lastError == ERROR_LOCK_VIOLATION))
- {
- //shadowFilename already contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat"
+ //assemble error message...
+ wxString errorMessage = wxString(_("Error copying file:")) + wxT("\n\"") + sourceFile.c_str() + wxT("\" ->\n\"") + targetFile.c_str() + wxT("\"") +
+ wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
- Zstring shadowFilename;
- try
- {
- shadowFilename = shadowCopyHandler->makeShadowCopy(sourceFile); //throw (FileError)
- }
- catch (const FileError& e)
- {
- wxString errorMsg = _("Error copying locked file %x!");
- errorMsg.Replace(wxT("%x"), wxString(wxT("\"")) + zToWx(sourceFile) + wxT("\""));
- errorMsg += wxT("\n\n") + e.msg();
- throw FileError(errorMsg);
- }
+ //if file is locked (try to) use Windows Volume Shadow Copy Service
+ if (lastError == ERROR_SHARING_VIOLATION ||
+ lastError == ERROR_LOCK_VIOLATION)
+ throw ErrorFileLocked(errorMessage);
- return copyFile(shadowFilename, //transferred bytes is automatically reset when new file is copied
- targetFile,
- copyFileSymLinks,
- copyFilePermissions,
- NULL,
- callback);
+ if (lastError == ERROR_FILE_EXISTS) //if target is existing this functions is expected to throw ErrorTargetExisting!!!
+ {
+ guardTarget.Dismiss(); //don't delete file that existed previously!
+ throw ErrorTargetExisting(errorMessage);
}
- //assemble error message...
- wxString errorMessage = wxString(_("Error copying file:")) + wxT("\n\"") + sourceFile.c_str() + wxT("\" ->\n\"") + targetFile.c_str() + wxT("\"") +
- wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
+ if (lastError == ERROR_PATH_NOT_FOUND)
+ throw ErrorTargetPathMissing(errorMessage);
try //add more meaningful message
{
//trying to copy > 4GB file to FAT/FAT32 volume gives obscure ERROR_INVALID_PARAMETER (FAT can indeed handle files up to 4 Gig, tested!)
if (lastError == ERROR_INVALID_PARAMETER &&
dst::isFatDrive(targetFile) &&
- getFilesize(sourceFile) >= (wxULongLong(1024 * 1024 * 1024)*=4)) //throw (FileError)
+ getFilesize(sourceFile) >= wxULongLong(1024 * 1024 * 1024) * 4) //throw (FileError)
errorMessage += wxT("\nFAT volume cannot store file larger than 4 gigabyte!");
}
catch(...) {}
@@ -1786,69 +1572,283 @@ void ffs3::copyFile(const Zstring& sourceFile,
throw FileError(errorMessage);
}
- //rename temporary file: do not add anything else here (note specific error handing)
- ffs3::renameFile(temporary, targetFile);
-
- guardTempFile.Dismiss(); //no need to delete temp file anymore
-
- Loki::ScopeGuard guardTargetFile = Loki::MakeGuard(&ffs3::removeFile, targetFile);
-
- //copy creation date (last modification date is REDUNDANTLY written, too, even for symlinks)
- copyFileTimes(sourceFile, targetFile, !copyFileSymLinks); //throw (FileError)
-
- if (copyFilePermissions)
- copyObjectPermissions(sourceFile, targetFile, !copyFileSymLinks); //throw FileError()
+ //adapt file modification time:
+ copyFileTimes(sourceFile, targetFile, true); //throw (FileError)
- guardTargetFile.Dismiss();
+ guardTarget.Dismiss(); //target has been created successfully!
}
-#elif defined FFS_LINUX
-void ffs3::copyFile(const Zstring& sourceFile,
- const Zstring& targetFile,
- bool copyFileSymLinks,
- bool copyFilePermissions,
- CopyFileCallback* callback)
+void rawCopyWinOptimized(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ CallbackCopyFile* callback) //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked)
{
- using ffs3::CopyFileCallback;
+ /*
+ 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()
+ */
- //symbolic link handling
- if (copyFileSymLinks &&
- symlinkExists(sourceFile))
+ //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)
{
- return copySymlinkInternal(sourceFile, targetFile, SYMLINK_TYPE_FILE, copyFilePermissions); //throw (FileError)
+ 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"
- //begin of regular file copy
- struct stat fileInfo;
- if (::stat(sourceFile.c_str(), &fileInfo) != 0) //read file attributes from source file (resolving symlinks)
+
+ 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());
}
- //open sourceFile for reading
- FileInput fileIn(sourceFile); //throw FileError()
+ 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
- const Zstring temporary = createTempName(targetFile); //use temporary file until a correct date has been set
+ 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!"));
+ }
+ }
- //ensure cleanup (e.g. network drop): call BEFORE creating fileOut object!
- Loki::ScopeGuard guardTempFile = Loki::MakeGuard(&ffs3::removeFile, temporary);
+ 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]);
- FileOutput fileOut(temporary); //throw FileError()
+ 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);
+ }
- const size_t BUFFER_SIZE = 512 * 1024; //512 kb seems to be a reasonable buffer size
- static const boost::scoped_array<char> memory(new char[BUFFER_SIZE]);
+ LPVOID read;
+ LPVOID write;
+ } context;
//copy contents of sourceFile to targetFile
wxULongLong totalBytesTransferred;
+
+ bool eof = false;
do
{
- const size_t bytesRead = fileIn.read(memory.get(), BUFFER_SIZE); //throw FileError()
+ DWORD bytesRead = 0;
- fileOut.write(memory.get(), bytesRead); //throw FileError()
+ 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;
@@ -1856,33 +1856,205 @@ void ffs3::copyFile(const Zstring& sourceFile,
if (callback != NULL)
switch (callback->updateCopyStatus(totalBytesTransferred))
{
- case CopyFileCallback::CONTINUE:
+ case CallbackCopyFile::CONTINUE:
break;
- case CopyFileCallback::CANCEL: //a user aborted operation IS an error condition!
+ 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 (!fileIn.eof());
+ while (!eof);
- //close output stream before changing attributes
- fileOut.close();
- //rename temporary file
- ffs3::renameFile(temporary, targetFile);
- guardTempFile.Dismiss();
+ 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;
- //ensure cleanup:
- Loki::ScopeGuard guardTargetFile = Loki::MakeGuard(&ffs3::removeFile, targetFile);
+ 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
+
+
+void rawCopyStream(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ CallbackCopyFile* callback) //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting)
+{
+ Loki::ScopeGuard guardTarget = Loki::MakeGuard(&removeFile, targetFile); //transactional behavior: place guard before lifetime of FileOutput
+ try
+ {
+ //open sourceFile for reading
+ FileInput fileIn(sourceFile); //throw (FileError)
+
+ //create targetFile and open it for writing
+ FileOutput fileOut(targetFile, FileOutput::ACC_CREATE_NEW); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting)
+
+ const size_t BUFFER_SIZE = 512 * 1024; //512 kb seems to be a reasonable buffer size
+ static const boost::scoped_array<char> memory(new char[BUFFER_SIZE]);
+
+ //copy contents of sourceFile to targetFile
+ wxULongLong totalBytesTransferred;
+ do
+ {
+ const size_t bytesRead = fileIn.read(memory.get(), BUFFER_SIZE); //throw (FileError)
+
+ fileOut.write(memory.get(), bytesRead); //throw (FileError)
+
+ 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 (!fileIn.eof());
+ }
+ catch(ErrorTargetExisting&)
+ {
+ guardTarget.Dismiss(); //don't delete file that existed previously!
+ throw;
+ }
//adapt file modification time:
copyFileTimes(sourceFile, targetFile, true); //throw (FileError)
+ guardTarget.Dismiss(); //target has been created successfully!
+}
+
+
+inline
+void copyFileImpl(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ CallbackCopyFile* callback) //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked)
+{
+#ifdef FFS_WIN
+ /*
+ rawCopyWinApi() rawCopyWinOptimized()
+ -------------------------------------
+ Attributes YES YES
+ Filetimes YES YES
+ ADS YES YES
+ Encrypted YES YES
+ Compressed NO YES
+ Sparse NO YES
+ PERF - 6% 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)
+#endif
+}
+}
+
+
+void ffs3::copyFile(const Zstring& sourceFile, //throw (FileError: ErrorTargetPathMissing, ErrorFileLocked);
+ const Zstring& targetFile,
+ bool copyFilePermissions,
+ CallbackCopyFile* callback)
+{
+ Zstring temporary = targetFile + ffs3::TEMP_FILE_ENDING; //use temporary file until a correct date has been set
+ Loki::ScopeGuard guardTempFile = Loki::MakeGuard(&removeFile, boost::cref(temporary)); //transactional behavior: ensure cleanup (e.g. network drop) -> cref [!]
+
+ //raw file copy
+ try
+ {
+ copyFileImpl(sourceFile, temporary, callback); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked)
+ }
+ catch (ErrorTargetExisting&)
+ {
+ //determine non-used temp file name "first":
+ //using optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses
+ temporary = createTempName(targetFile);
+
+ //retry
+ copyFileImpl(sourceFile, temporary, callback); //throw (FileError)
+ }
+
+ //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite
+ if (callback) callback->deleteTargetFile(targetFile);
+
+ //rename temporary file:
+ //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick!
+ renameFile(temporary, targetFile); //throw (FileError)
+
+ guardTempFile.Dismiss();
+ Loki::ScopeGuard guardTargetFile = Loki::MakeGuard(&removeFile, targetFile);
+
+ //perf: interestingly it is much faster to apply file times BEFORE renaming temporary!
+
//set file permissions
if (copyFilePermissions)
- copyObjectPermissions(sourceFile, targetFile, true); //throw FileError()
+ copyObjectPermissions(sourceFile, targetFile, true); //throw (FileError)
guardTargetFile.Dismiss(); //target has been created successfully!
}
-#endif
diff --git a/shared/file_handling.h b/shared/file_handling.h
index 4ec9c6d2..1d68c399 100644
--- a/shared/file_handling.h
+++ b/shared/file_handling.h
@@ -7,26 +7,20 @@
#ifndef FILE_HANDLING_H_INCLUDED
#define FILE_HANDLING_H_INCLUDED
+#include <wx/longlong.h>
#include "zstring.h"
#include "file_error.h"
-#include <wx/longlong.h>
-
-#ifdef FFS_WIN
-#include "shadow.h"
-#endif
namespace ffs3
{
-struct RemoveDirCallback;
-struct MoveFileCallback;
-struct CopyFileCallback;
-
+struct CallbackRemoveDir;
+struct CallbackMoveFile;
+struct CallbackCopyFile;
-Zstring getFormattedDirectoryName(const Zstring& dirname);
-bool fileExists( const Zstring& filename); //throw() replaces wxFileExists()!
-bool dirExists( const Zstring& dirname); //throw() replaces wxDirExists(): optional 'cause wxDirExists treats symlinks correctly
+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
@@ -43,61 +37,61 @@ ResponseSameVol onSameVolume(const Zstring& folderLeft, const Zstring& folderRig
//copy file or directory create/last change date,
void copyFileTimes(const Zstring& sourceDir, const Zstring& targetDir, bool derefSymlinks); //throw (FileError)
-//copy filesystem permissions: probably requires admin rights
-void copyObjectPermissions(const Zstring& source, const Zstring& target, bool derefSymlinks); //throw FileError();
-
//symlink handling: always evaluate target
wxULongLong getFilesize(const Zstring& filename); //throw (FileError)
//file handling
void removeFile(const Zstring& filename); //throw (FileError)
-void removeDirectory(const Zstring& directory, RemoveDirCallback* callback = NULL); //throw (FileError)
+void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = NULL); //throw (FileError)
//rename file or directory: no copying!!!
void renameFile(const Zstring& oldName, const Zstring& newName); //throw (FileError);
-//move source to target; expectations: target not existing, all super-directories of target exist
-void moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFileCallback* callback = NULL); //throw (FileError);
+//move source to target; expectations: all super-directories of target exist
+//"ignoreExisting": if target already exists, source is deleted
+void moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ignoreExisting, CallbackMoveFile* callback); //throw (FileError);
//move source to target including subdirectories
-//"ignoreExistingDirs": existing directories will be enhanced as long as this is possible without overwriting files
-void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExistingDirs, MoveFileCallback* callback = NULL); //throw (FileError);
+//"ignoreExisting": existing directories and files will be enriched
+void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback); //throw (FileError);
//creates superdirectories automatically:
-void createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyDirectorySymLinks, bool copyFilePermissions); //throw (FileError);
+void createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw (FileError);
void createDirectory(const Zstring& directory); //throw (FileError); -> function overload avoids default parameter ambiguity issues!
-void copyFile(const Zstring& sourceFile, //throw (FileError);
+void copyFile(const Zstring& sourceFile, //throw (FileError: ErrorTargetPathMissing, ErrorFileLocked (Windows-only));
const Zstring& targetFile,
- bool copyFileSymLinks,
bool copyFilePermissions,
-#ifdef FFS_WIN
- shadow::ShadowCopy* shadowCopyHandler, //supply handler for making shadow copies, may be NULL
-#endif
- CopyFileCallback* callback); //may be NULL
+ CallbackCopyFile* callback); //may be NULL
//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop.
// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending:
const Zstring TEMP_FILE_ENDING = Zstr(".ffs_tmp");
+enum SymlinkType
+{
+ SYMLINK_TYPE_FILE,
+ SYMLINK_TYPE_DIR
+};
+void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, SymlinkType type, bool copyFilePermissions); //throw (FileError)
//----------- callbacks ---------------
-struct RemoveDirCallback
+struct CallbackRemoveDir
{
- virtual ~RemoveDirCallback() {}
+ virtual ~CallbackRemoveDir() {}
virtual void requestUiRefresh(const Zstring& currentObject) = 0;
};
-struct MoveFileCallback //callback functionality
+struct CallbackMoveFile //callback functionality
{
- virtual ~MoveFileCallback() {}
+ virtual ~CallbackMoveFile() {}
enum Response
{
@@ -108,9 +102,13 @@ struct MoveFileCallback //callback functionality
};
-struct CopyFileCallback //callback functionality
+struct CallbackCopyFile //callback functionality
{
- virtual ~CopyFileCallback() {}
+ virtual ~CallbackCopyFile() {}
+
+ //if target is existing user needs to implement deletion: copyFile() NEVER deletes target if already existing!
+ //at this point full read access on source had been proven, so it's safe to delete it.
+ virtual void deleteTargetFile(const Zstring& targetFile) = 0; //may throw exceptions
enum Response
{
diff --git a/shared/file_io.cpp b/shared/file_io.cpp
index e6de77bb..0afe17dd 100644
--- a/shared/file_io.cpp
+++ b/shared/file_io.cpp
@@ -5,9 +5,9 @@
// **************************************************************************
//
#include "file_io.h"
-#include <wx/intl.h>
#include "string_conv.h"
#include "system_func.h"
+#include "i18n.h"
#ifdef FFS_WIN
#include "long_path_prefix.h"
@@ -18,7 +18,15 @@
using namespace ffs3;
-FileInput::FileInput(const Zstring& filename) : //throw FileError()
+FileInput::FileInput(FileHandle handle, const Zstring& filename) :
+#ifdef FFS_WIN
+ eofReached(false),
+#endif
+ fileHandle(handle),
+ filename_(filename) {}
+
+
+FileInput::FileInput(const Zstring& filename) : //throw (FileError, ErrorNotExisting)
#ifdef FFS_WIN
eofReached(false),
#endif
@@ -63,19 +71,19 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError()
if (lastError == ERROR_FILE_NOT_FOUND ||
lastError == ERROR_PATH_NOT_FOUND)
throw ErrorNotExisting(errorMessage);
- else
- throw FileError(errorMessage);
+
+ throw FileError(errorMessage);
}
#elif defined FFS_LINUX
- fileHandle = ::fopen(filename.c_str(), "rb,type=record,noseek"); //utilize UTF-8 filename
+ fileHandle = ::fopen(filename.c_str(), "r,type=record,noseek"); //utilize UTF-8 filename
if (fileHandle == NULL)
{
const int lastError = errno;
const wxString& errorMessage = wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") + wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
if (lastError == ENOENT)
throw ErrorNotExisting(errorMessage);
- else
- throw FileError(errorMessage);
+
+ throw FileError(errorMessage);
}
#endif
}
@@ -91,25 +99,28 @@ FileInput::~FileInput()
}
-size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw FileError()
+size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw (FileError)
{
#ifdef FFS_WIN
DWORD bytesRead = 0;
- if (!::ReadFile(
- fileHandle, //__in HANDLE hFile,
- buffer, //__out LPVOID lpBuffer,
- static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead,
- &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead,
- NULL) //__inout_opt LPOVERLAPPED lpOverlapped
- || bytesRead > bytesToRead) //must be fulfilled
+ if (!::ReadFile(fileHandle, //__in HANDLE hFile,
+ buffer, //__out LPVOID lpBuffer,
+ static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead,
+ &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead,
+ NULL)) //__inout_opt LPOVERLAPPED lpOverlapped
throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
wxT("\n\n") + ffs3::getLastErrorFormatted());
+ if (bytesRead > bytesToRead)
+ throw FileError(wxString(_("Error reading file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
+ wxT("\n\n") + wxT("buffer overflow"));
+
if (bytesRead < bytesToRead)
- eofReached = true;
+ eofReached = bytesRead < bytesToRead;
return bytesRead;
+
#elif defined FFS_LINUX
const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle);
if (::ferror(fileHandle) != 0)
@@ -130,49 +141,83 @@ bool FileInput::eof() //end of file reached
}
-FileOutput::FileOutput(const Zstring& filename) : //throw FileError()
+FileOutput::FileOutput(FileHandle handle, const Zstring& filename) : fileHandle(handle), filename_(filename) {}
+
+
+FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw (FileError, ErrorTargetPathMissing, ErrorTargetExisting)
filename_(filename)
{
#ifdef FFS_WIN
fileHandle = ::CreateFile(ffs3::applyLongPathPrefix(filename).c_str(),
GENERIC_WRITE,
- FILE_SHARE_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //note: FILE_SHARE_DELETE is required to rename file while handle is open!
NULL,
- CREATE_ALWAYS,
+ access == ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
- throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
- wxT("\n\n") + ffs3::getLastErrorFormatted());
+ {
+ const DWORD lastError = ::GetLastError();
+ const wxString& errorMessage = wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + 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);
+ }
+
#elif defined FFS_LINUX
- fileHandle = ::fopen(filename.c_str(), "wb,type=record,noseek"); //utilize UTF-8 filename
- if (!fileHandle)
- throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
- wxT("\n\n") + ffs3::getLastErrorFormatted());
+ fileHandle = ::fopen(filename.c_str(),
+ //GNU extension: https://www.securecoding.cert.org/confluence/display/cplusplus/FIO03-CPP.+Do+not+make+assumptions+about+fopen()+and+file+creation
+ access == ACC_OVERWRITE ? "w,type=record,noseek" : "wx,type=record,noseek");
+ if (fileHandle == NULL)
+ {
+ const int lastError = errno;
+ const wxString& errorMessage = wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
+ wxT("\n\n") + ffs3::getLastErrorFormatted(lastError);
+ if (lastError == EEXIST)
+ throw ErrorTargetExisting(errorMessage);
+
+ if (lastError == ENOENT)
+ throw ErrorTargetPathMissing(errorMessage);
+
+ throw FileError(errorMessage);
+ }
#endif
}
FileOutput::~FileOutput()
{
- close(); //may be called more than once
+#ifdef FFS_WIN
+ ::CloseHandle(fileHandle);
+#elif defined FFS_LINUX
+ ::fclose(fileHandle); //NEVER allow passing NULL to fclose! -> crash!
+#endif
}
-void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError()
+void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw (FileError)
{
#ifdef FFS_WIN
DWORD bytesWritten = 0;
- if (!::WriteFile(
- fileHandle, //__in HANDLE hFile,
- buffer, //__out LPVOID lpBuffer,
- static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToRead,
- &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
- NULL) //__inout_opt LPOVERLAPPED lpOverlapped
- || bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes!
+ if (!::WriteFile(fileHandle, //__in HANDLE hFile,
+ buffer, //__out LPVOID lpBuffer,
+ static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite,
+ &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
+ NULL)) //__inout_opt LPOVERLAPPED lpOverlapped
throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message!
+
+ if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes!
+ throw FileError(wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(filename_) + wxT("\"") +
+ wxT("\n\n") + wxT("incomplete write"));
+
#elif defined FFS_LINUX
const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle);
if (::ferror(fileHandle) != 0 || bytesWritten != bytesToWrite)
@@ -180,17 +225,3 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
wxT("\n\n") + ffs3::getLastErrorFormatted() + wxT(" (w)")); //w -> distinguish from fopen error message!
#endif
}
-
-
-void FileOutput::close() //close file stream
-{
- if (fileHandle != NULL) //NEVER allow passing NULL to fclose! -> crash!
- {
-#ifdef FFS_WIN
- ::CloseHandle(fileHandle);
-#elif defined FFS_LINUX
- ::fclose(fileHandle);
-#endif
- fileHandle = NULL;
- }
-}
diff --git a/shared/file_io.h b/shared/file_io.h
index 0781595b..a1460f53 100644
--- a/shared/file_io.h
+++ b/shared/file_io.h
@@ -22,22 +22,27 @@ namespace ffs3
{
//file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
+#ifdef FFS_WIN
+typedef HANDLE FileHandle;
+#elif defined FFS_LINUX
+typedef FILE* FileHandle;
+#endif
+
class FileInput
{
public:
- FileInput(const Zstring& filename); //throw (FileError, ErrorNotExisting)
+ FileInput(const Zstring& filename); //throw (FileError: ErrorNotExisting)
+ FileInput(FileHandle handle, const Zstring& filename); //takes ownership!
~FileInput();
- size_t read(void* buffer, size_t bytesToRead); //throw FileError(); returns actual number of bytes read
+ size_t read(void* buffer, size_t bytesToRead); //throw (FileError); returns actual number of bytes read
bool eof(); //end of file reached
private:
#ifdef FFS_WIN
- HANDLE fileHandle;
bool eofReached;
-#elif defined FFS_LINUX
- FILE* fileHandle;
#endif
+ FileHandle fileHandle;
const Zstring filename_;
};
@@ -45,17 +50,19 @@ private:
class FileOutput
{
public:
- FileOutput(const Zstring& filename); //throw FileError()
+ enum AccessFlag
+ {
+ ACC_OVERWRITE,
+ ACC_CREATE_NEW
+ };
+ FileOutput(const Zstring& filename, AccessFlag access); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting)
+ FileOutput(FileHandle handle, const Zstring& filename); //takes ownership!
~FileOutput();
- void write(const void* buffer, size_t bytesToWrite); //throw FileError()
- void close(); //close file stream
+ void write(const void* buffer, size_t bytesToWrite); //throw (FileError)
+
private:
-#ifdef FFS_WIN
- HANDLE fileHandle;
-#elif defined FFS_LINUX
- FILE* fileHandle;
-#endif
+ FileHandle fileHandle;
const Zstring filename_;
};
@@ -65,11 +72,11 @@ private:
class FileInputStream : public wxInputStream
{
public:
- FileInputStream(const Zstring& filename) : //throw FileError()
+ FileInputStream(const Zstring& filename) : //throw (FileError)
fileObj(filename) {}
private:
- virtual size_t OnSysRead(void* buffer, size_t bufsize) //throw FileError()
+ virtual size_t OnSysRead(void* buffer, size_t bufsize) //throw (FileError)
{
return fileObj.read(buffer, bufsize);
}
@@ -81,11 +88,11 @@ private:
class FileOutputStream : public wxOutputStream
{
public:
- FileOutputStream(const Zstring& filename) : //throw FileError()
- fileObj(filename) {}
+ FileOutputStream(const Zstring& filename) : //throw (FileError)
+ fileObj(filename, FileOutput::ACC_OVERWRITE) {}
private:
- virtual size_t OnSysWrite(const void* buffer, size_t bufsize) //throw FileError()
+ virtual size_t OnSysWrite(const void* buffer, size_t bufsize) //throw (FileError)
{
fileObj.write(buffer, bufsize);
return bufsize;
diff --git a/shared/file_traverser.cpp b/shared/file_traverser.cpp
index f0777898..2af0f4af 100644
--- a/shared/file_traverser.cpp
+++ b/shared/file_traverser.cpp
@@ -8,7 +8,6 @@
#include <limits>
#include "system_constants.h"
#include "system_func.h"
-#include <wx/intl.h>
#include "string_conv.h"
#include "assert_static.h"
#include "symlink_target.h"
diff --git a/shared/localization.cpp b/shared/i18n.cpp
index a412df1e..537d9fb4 100644
--- a/shared/localization.cpp
+++ b/shared/i18n.cpp
@@ -4,32 +4,32 @@
// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
// **************************************************************************
//
-#include "localization.h"
+#include "i18n.h"
+#include <fstream>
+#include <map>
+#include <wx/ffile.h>
+#include <wx/intl.h>
#include <wx/msgdlg.h>
#include "../shared/standard_paths.h"
#include "../shared/string_conv.h"
#include "system_constants.h"
-#include <fstream>
-#include <map>
-#include <wx/ffile.h>
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
-#if wxCHECK_VERSION(2, 9, 1)
-#include <boost/cstdint.hpp>
-#include <wx/translation.h>
-#include <cstdlib>
-#endif
-
-using ffs3::CustomLocale;
using ffs3::LocalizationInfo;
-//_("Browse") <- dummy string for wxDirPickerCtrl to be recognized by automatic text extraction!
-
namespace
{
//will receive their proper value in CustomLocale::CustomLocale()
wxString THOUSANDS_SEPARATOR = wxT(",");
wxString DECIMAL_POINT = wxT(".");
+
+typedef std::map<wxString, wxString> Translation;
+Translation activeTranslation; //map original text |-> translation
+
+int activeLanguage = wxLANGUAGE_ENGLISH;
}
@@ -45,25 +45,13 @@ wxString ffs3::getDecimalPoint()
}
-const std::vector<ffs3::LocInfoLine>& LocalizationInfo::getMapping()
+const std::vector<ffs3::LocInfoLine>& LocalizationInfo::get()
{
static LocalizationInfo instance;
return instance.locMapping;
}
-namespace
-{
-struct CompareByName : public std::binary_function<ffs3::LocInfoLine, ffs3::LocInfoLine, bool>
-{
- bool operator()(const ffs3::LocInfoLine& lhs, const ffs3::LocInfoLine& rhs) const
- {
- return lhs.languageName < rhs.languageName;
- }
-};
-}
-
-
LocalizationInfo::LocalizationInfo()
{
ffs3::LocInfoLine newEntry;
@@ -242,8 +230,6 @@ LocalizationInfo::LocalizationInfo()
newEntry.translatorName = wxT("Simon Park");
newEntry.languageFlag = wxT("south_korea.png");
locMapping.push_back(newEntry);
-
- //std::sort(locMapping.begin(), locMapping.end(), CompareByName());
}
@@ -330,7 +316,7 @@ int mapLanguageDialect(int language)
//case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
//case wxLANGUAGE_KOREAN:
- //variants of wxLANGUAGE_ARABIC (also needed to detect RTL languages)
+ //variants of wxLANGUAGE_ARABIC
case wxLANGUAGE_ARABIC_ALGERIA:
case wxLANGUAGE_ARABIC_BAHRAIN:
case wxLANGUAGE_ARABIC_EGYPT:
@@ -472,18 +458,13 @@ private:
};
-typedef std::map<wxString, wxString> TranslationMap; //map original text |-> translation
-
-void loadTranslation(const wxString& filename, TranslationMap& trans) //empty translation on error
+void loadTranslation(const wxString& filename, Translation& trans) //empty translation on error
{
trans.clear();
UnicodeFileReader langFile(ffs3::getResourceDir() + wxT("Languages") + ffs3::zToWx(common::FILE_NAME_SEPARATOR) + filename);
if (langFile.isOkay())
{
- //save encoding info: required by mo file generator
- trans.insert(std::make_pair(wxEmptyString, wxT("Content-Type: text/plain; charset=UTF-8\n")));
-
int rowNumber = 0;
wxString original;
wxString tmpString;
@@ -506,195 +487,265 @@ void loadTranslation(const wxString& filename, TranslationMap& trans) //empty tr
}
-#if wxCHECK_VERSION(2, 9, 1)
-//this whole abomination is required to support language formats other than "mo" in wxWidgets v2.9
-class FFSTranslationLoader : public wxTranslationsLoader
+void ffs3::setLanguage(int language)
{
-public:
- static const wxString domainName()
+ static class StaticInit
{
- return wxT("FFS");
- }
+ public:
+ StaticInit() : loc(wxLANGUAGE_DEFAULT) //wxLocale: we need deferred initialization, sigh...
+ {
+ //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
+ const lconv* localInfo = ::localeconv();
- FFSTranslationLoader(const TranslationMap& trans, wxLanguage langId) : langId_(langId)
- {
- //generate mo file: http://www.gnu.org/software/hello/manual/gettext/MO-Files.html
+ //actually these two parameters are language dependent, but we take system setting to handle all kinds of language derivations
+ THOUSANDS_SEPARATOR = wxString::FromUTF8(localInfo->thousands_sep);
+ DECIMAL_POINT = wxString::FromUTF8(localInfo->decimal_point);
- std::string binaryStream;
+ // why not working?
+ // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).thousands_sep();
+ // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).decimal_point();
+ }
+ private:
+ wxLocale loc; //required for RTL language support (and nothing else)
+ } dummy;
- const size_t offsetTableOrig = sizeof(wxMsgCatalogHeader);
- const size_t offsetTableTrans = offsetTableOrig + trans.size() * sizeof(wxMsgTableEntry);
- const size_t offsetTableString = offsetTableTrans + trans.size() * sizeof(wxMsgTableEntry);
+ activeLanguage = language;
- wxMsgCatalogHeader header =
- {
- 0x950412de, //magic number (save in this machine's byte order)
- 0, //revision
- trans.size(), //numStrings
- offsetTableOrig, //ofsOrigTable
- offsetTableTrans, //ofsTransTable
- 0, //nHashSize
- 0, //ofsHashTable
- };
- writeCobject(binaryStream, header);
-
- std::string tableOrig;
- std::string tableTrans;
- std::string stringsList;
- for (TranslationMap::const_iterator i = trans.begin(); i != trans.end(); ++i)
- {
+ //default: english
+ wxString languageFile;
-#ifndef _MSC_VER
-#warning redundant UTF8 conversion!!!
-#endif
- std::string origString = i->first.ToUTF8();
- const wxMsgTableEntry origEntry = {origString.length(), offsetTableString + stringsList.size()};
- writeCobject(tableOrig, origEntry);
- stringsList.append(origString.c_str(), origString.length() + 1); //include NULL-termination
-
-#ifndef _MSC_VER
-#warning redundant UTF8 conversion!!!
-#endif
- std::string transString = i->second.ToUTF8();
- const wxMsgTableEntry transEntry = {transString.length(), offsetTableString + stringsList.size()};
- writeCobject(tableTrans, transEntry);
- stringsList.append(transString.c_str(), transString.length() + 1); //include NULL-termination
+ //(try to) retrieve language filename
+ const int mappedLanguage = mapLanguageDialect(language);
+ for (std::vector<LocInfoLine>::const_iterator i = LocalizationInfo::get().begin(); i != LocalizationInfo::get().end(); ++i)
+ if (i->languageID == mappedLanguage)
+ {
+ languageFile = i->languageFile;
+ break;
}
- binaryStream += tableOrig;
- binaryStream += tableTrans;
- binaryStream += stringsList;
- buffer = wxScopedCharBuffer::CreateOwned(static_cast<char*>(::malloc(binaryStream.size())), binaryStream.size()); //takes buffer ownership, calls ::free()
- std::copy(binaryStream.begin(), binaryStream.end(), buffer.data());
+ //load language file into buffer
+ activeTranslation.clear();
+ if (!languageFile.empty())
+ {
+ loadTranslation(languageFile, activeTranslation); //empty translation on error
+ if (activeTranslation.empty())
+ {
+ wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + languageFile + wxT("\""), _("Error"), wxOK | wxICON_ERROR);
+ activeLanguage = wxLANGUAGE_ENGLISH; //reset to english language to show this error just once
+ }
}
+ else
+ ; //if languageFile is empty texts will be english per default
+}
- virtual wxMsgCatalog* LoadCatalog(const wxString& domain, const wxString& lang)
- {
- if (domain != domainName() || lang != wxLocale::GetLanguageCanonicalName(langId_)) //avoid superfluous calls by wxWidgets framework
- return NULL;
- return wxMsgCatalog::CreateFromData(buffer, domain);
- }
+int ffs3::getLanguage()
+{
+ return activeLanguage;
+}
- virtual wxArrayString GetAvailableTranslations(const wxString& domain) const
- {
- wxArrayString output;
- if (domain == domainName())
- output.Add(wxLocale::GetLanguageCanonicalName(langId_));
- return output;
- }
-private:
- struct wxMsgTableEntry
- {
- boost::uint32_t nLen, // length of the string
- ofsString; // pointer to the string
- };
+int ffs3::retrieveSystemLanguage()
+{
+ return wxLocale::GetSystemLanguage();
+}
- // header of a .mo file
- struct wxMsgCatalogHeader
- {
- boost::uint32_t magic, // offset +00: magic id
- revision, // +04: revision
- numStrings, // +08: number of strings in the file
- ofsOrigTable, // +0C: start of original string table
- ofsTransTable, // +10: start of translated string table
- nHashSize, // +14: hash table size
- ofsHashTable; // +18: offset of hash table start
- };
- template <class T>
- void writeCobject(std::string& str, T obj)
- {
- str.append(reinterpret_cast<const char*>(&obj), sizeof(obj));
- }
- wxScopedCharBuffer buffer; //raw data in mo file format
- const wxLanguage langId_;
+
+/*
+typedef Zbase<wchar_t> Wstring;
+
+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=";
+
+
+template <class T>
+struct Expr
+{
+ typedef T ValueType;
+ virtual ValueType eval() const = 0;
};
-#endif
-CustomLocale& CustomLocale::getInstance()
+template <class In, class Out, class BiOp>
+struct GenericBiExp : public Out
{
- static CustomLocale instance;
- return instance;
+ 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_;
+};
+
+template <class Out, class In, class BiOp>
+inline
+GenericBiExp<In, Out, BiOp> makeBiExp(const In& lhs, const In& rhs, BiOp biop)
+{
+ return GenericBiExp<In, Out, BiOp>(lhs, rhs, biop);
}
+template <class Out>
+struct TernaryExp : public Out
+{
+ TernaryExp(const Expr<bool>& ifExp, const Out& thenExp, const Out& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) {}
+ virtual typename Out::ValueType eval() const { return ifExp_.eval() ? thenExp_.eval() : elseExp_.eval(); }
+ const Expr<bool>& ifExp_;
+ const Out& thenExp_;
+ const Out& elseExp_;
+};
+
+struct LiteralNumberEx : public Expr<int>
+{
+ LiteralNumberEx(int n) : n_(n) {}
+ virtual ValueType eval() const { return n_; }
+ int n_;
+};
-class Translation : public TranslationMap {};
+struct NumberN : public Expr<int>
+{
+ NumberN(int& n) : n_(n) {}
+ virtual ValueType eval() const { return n_; }
+ int& n_;
+};
-CustomLocale::CustomLocale() :
- translationDB(new Translation),
- currentLanguage(wxLANGUAGE_ENGLISH)
+class PluralForm
{
- Init(wxLANGUAGE_DEFAULT); //setting a different language needn't be supported on all systems!
+public:
+ struct ParserError {};
- //actually these two parameters are language dependent, but we take system setting to handle all kinds of language derivations
- const lconv* localInfo = localeconv();
- THOUSANDS_SEPARATOR = wxString::FromUTF8(localInfo->thousands_sep);
- DECIMAL_POINT = wxString::FromUTF8(localInfo->decimal_point);
+ 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)
+ {
+ Wstring tmp;
+ std::remove_copy_if(phrase.begin(), phrase.end(), std::back_inserter(tmp), std::iswspace);
- // why not working?
- // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).thousands_sep();
- // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).decimal_point();
-}
+ size_t pos = tmp.find(TK_FORM_COUNT);
+ if (pos == Wstring::npos)
+ throw ParserError();
+ count = tmp.substr(pos + TK_FORM_COUNT.size()).toNumber<int>();
-CustomLocale::~CustomLocale() {} //non-inline destructor for std::auto_ptr to work with forward declaration
+ pos = tmp.find(TK_PHRASE_BEGIN);
+ if (pos == Wstring::npos)
+ throw ParserError();
+ expr = &parseNumber(tmp.substr(pos + TK_PHRASE_BEGIN.size()));
+ }
-void CustomLocale::setLanguage(int language)
-{
- currentLanguage = static_cast<wxLanguage>(language);
+ int formCount() const { return count; }
- //default: english
- wxString languageFile;
+ int getForm(int n) const { n_ = n ; return expr->eval(); }
- //(try to) retrieve language filename
- const int mappedLanguage = mapLanguageDialect(language);
- for (std::vector<LocInfoLine>::const_iterator i = LocalizationInfo::getMapping().begin(); i != LocalizationInfo::getMapping().end(); ++i)
- if (i->languageID == mappedLanguage)
- {
- languageFile = i->languageFile;
- break;
- }
+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();
+ }
- //load language file into buffer
- translationDB->clear();
- if (!languageFile.empty())
+ const Expr<int>& parseNumber(const Wstring& phrase)
{
- loadTranslation(languageFile, *translationDB); //empty translation on error
- if (translationDB->empty())
+ Wstring part1, part2;
+
+
+ if (trySplit(phrase, TK_TERNARY_QUEST, part1, part2, TO_LEFT))
{
- wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + languageFile + wxT("\""), _("Error"), wxOK | wxICON_ERROR);
- currentLanguage = wxLANGUAGE_ENGLISH; //reset to english language to show this error just once
+ 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)));
+ }
+ 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
+ {
+ 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>()));
}
}
- else
- ; //if languageFile is empty texts will be english per default
+ enum Associativity
+ {
+ TO_LEFT,
+ TO_RIGHT
+ };
+ 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)
+ {
+ dump.push_back(obj);
+ return *boost::any_cast<T>(&dump.back());
+ }
+ int count;
+ const Expr<int>* expr;
+ mutable int n_;
+
+ std::list<boost::any> dump; //manage polymorphc object lifetimes
+};
-#if wxCHECK_VERSION(2, 9, 1)
- wxTranslations::Set(new wxTranslations);
- wxTranslations::Get()->SetLoader(new FFSTranslationLoader(*translationDB, currentLanguage)); //ownership passed
- wxTranslations::Get()->SetLanguage(currentLanguage);
- wxTranslations::Get()->AddCatalog(FFSTranslationLoader::domainName(), wxLANGUAGE_ENGLISH_US);
- //... a little over design going on?!?
-#endif
-}
+ 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 wxChar* CustomLocale::GetString(const wxChar* szOrigString, const wxChar* szDomain) const
+
+
+wxString ffs3::translate(const wxString& original) //translate into currently selected language
{
//look for translation in buffer table
- const Translation::const_iterator i = translationDB->find(szOrigString);
- if (i != translationDB->end())
+ const Translation::const_iterator i = activeTranslation.find(original);
+ if (i != activeTranslation.end())
return i->second.c_str();
//fallback
- return szOrigString;
+ return original;
}
-
diff --git a/shared/localization.h b/shared/i18n.h
index 3ce7bf24..0026cdd5 100644
--- a/shared/localization.h
+++ b/shared/i18n.h
@@ -7,23 +7,14 @@
#ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED
-#include <wx/intl.h>
+#include <wx/string.h>
#include <vector>
-#include <memory>
-
-class Translation;
-
namespace ffs3
{
-//language independent global variables: just use operating system's default setting!
-wxString getThousandsSeparator();
-wxString getDecimalPoint();
-
-
struct LocInfoLine
{
- wxLanguage languageID;
+ int languageID;
wxString languageName;
wxString languageFile;
wxString translatorName;
@@ -33,7 +24,7 @@ struct LocInfoLine
class LocalizationInfo
{
public:
- static const std::vector<LocInfoLine>& getMapping();
+ static const std::vector<LocInfoLine>& get();
private:
LocalizationInfo();
@@ -44,27 +35,24 @@ private:
};
-class CustomLocale : public wxLocale
-{
-public:
- static CustomLocale& getInstance();
-
- void setLanguage(int language);
+//language independent global variables: just use operating system's default setting!
+wxString getThousandsSeparator();
+wxString getDecimalPoint();
- int getLanguage() const
- {
- return currentLanguage;
- }
+void setLanguage(int language);
+int getLanguage();
- virtual const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const;
+int retrieveSystemLanguage();
-private:
- CustomLocale();
- ~CustomLocale(); //non-inline destructor for std::auto_ptr to work with forward declaration -> superfluous in this case: singleton pattern!
+wxString translate(const wxString& original); //translate into currently selected language
- std::auto_ptr<Translation> translationDB; //careful with forward-declarations and auto_ptr! save in this case, 'cause full class info available
- wxLanguage currentLanguage;
-};
+//translate plural forms: "%x day|%x days"
+//returns "%x day" if n == 1; "%x days" else for english language
+wxString translate(const wxString& original, int n);
}
+//WXINTL_NO_GETTEXT_MACRO must be defined to deactivate wxWidgets underscore macro
+#define _(s) ffs3::translate(wxT(s))
+#define _P(s, n) ffs3::translate(wxT(s), n)
+
#endif // MISC_H_INCLUDED
diff --git a/shared/i18n_no_BOM.cpp b/shared/i18n_no_BOM.cpp
new file mode 100644
index 00000000..78f2b230
--- /dev/null
+++ b/shared/i18n_no_BOM.cpp
@@ -0,0 +1,751 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+#include "i18n.h"
+#include <fstream>
+#include <map>
+#include <wx/ffile.h>
+#include <wx/intl.h>
+#include <wx/msgdlg.h>
+#include "../shared/standard_paths.h"
+#include "../shared/string_conv.h"
+#include "system_constants.h"
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+
+using ffs3::LocalizationInfo;
+
+
+namespace
+{
+//will receive their proper value in CustomLocale::CustomLocale()
+wxString THOUSANDS_SEPARATOR = wxT(",");
+wxString DECIMAL_POINT = wxT(".");
+
+typedef std::map<wxString, wxString> Translation;
+Translation activeTranslation; //map original text |-> translation
+
+int activeLanguage = wxLANGUAGE_ENGLISH;
+}
+
+
+wxString ffs3::getThousandsSeparator()
+{
+ return THOUSANDS_SEPARATOR;
+}
+
+
+wxString ffs3::getDecimalPoint()
+{
+ return DECIMAL_POINT;
+}
+
+
+const std::vector<ffs3::LocInfoLine>& LocalizationInfo::get()
+{
+ static LocalizationInfo instance;
+ return instance.locMapping;
+}
+
+
+LocalizationInfo::LocalizationInfo()
+{
+ ffs3::LocInfoLine newEntry;
+
+ newEntry.languageID = wxLANGUAGE_CZECH;
+ newEntry.languageName = wxT("Čeština");
+ newEntry.languageFile = wxT("czech.lng");
+ newEntry.translatorName = wxT("ViCi");
+ newEntry.languageFlag = wxT("czechRep.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_GERMAN;
+ newEntry.languageName = wxT("Deutsch");
+ newEntry.languageFile = wxT("german.lng");
+ newEntry.translatorName = wxT("ZenJu");
+ newEntry.languageFlag = wxT("germany.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_ENGLISH_UK;
+ newEntry.languageName = wxT("English (UK)");
+ newEntry.languageFile = wxT("english_uk.lng");
+ newEntry.translatorName = wxT("Robert Readman");
+ newEntry.languageFlag = wxT("england.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_ENGLISH;
+ newEntry.languageName = wxT("English (US)");
+ newEntry.languageFile = wxT("");
+ newEntry.translatorName = wxT("ZenJu");
+ newEntry.languageFlag = wxT("usa.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_SPANISH;
+ newEntry.languageName = wxT("Español");
+ newEntry.languageFile = wxT("spanish.lng");
+ newEntry.translatorName = wxT("Alexis Martínez");
+ newEntry.languageFlag = wxT("spain.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_GREEK;
+ newEntry.languageName = wxT("Ελληνικά");
+ newEntry.languageFile = wxT("greek.lng");
+ newEntry.translatorName = wxT("Γιώργος Γιαγλής");
+ newEntry.languageFlag = wxT("greece.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_FRENCH;
+ newEntry.languageName = wxT("Français");
+ newEntry.languageFile = wxT("french.lng");
+ newEntry.translatorName = wxT("Jean-François Hartmann");
+ newEntry.languageFlag = wxT("france.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_ITALIAN;
+ newEntry.languageName = wxT("Italiano");
+ newEntry.languageFile = wxT("italian.lng");
+ newEntry.translatorName = wxT("Emmo");
+ newEntry.languageFlag = wxT("italy.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_HUNGARIAN;
+ newEntry.languageName = wxT("Magyar");
+ newEntry.languageFile = wxT("hungarian.lng");
+ newEntry.translatorName = wxT("Demon");
+ newEntry.languageFlag = wxT("hungary.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_DUTCH;
+ newEntry.languageName = wxT("Nederlands");
+ newEntry.languageFile = wxT("dutch.lng");
+ newEntry.translatorName = wxT("Dion van Lieshout");
+ newEntry.languageFlag = wxT("holland.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_POLISH;
+ newEntry.languageName = wxT("Polski");
+ newEntry.languageFile = wxT("polish.lng");
+ newEntry.translatorName = wxT("Wojtek Pietruszewski");
+ newEntry.languageFlag = wxT("poland.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_PORTUGUESE;
+ newEntry.languageName = wxT("Português");
+ newEntry.languageFile = wxT("portuguese.lng");
+ newEntry.translatorName = wxT("QuestMark");
+ newEntry.languageFlag = wxT("portugal.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_PORTUGUESE_BRAZILIAN;
+ newEntry.languageName = wxT("Português do Brasil");
+ newEntry.languageFile = wxT("portuguese_br.lng");
+ newEntry.translatorName = wxT("Edison Aranha");
+ newEntry.languageFlag = wxT("brazil.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_RUSSIAN;
+ newEntry.languageName = wxT("Pусский");
+ newEntry.languageFile = wxT("russian.lng");
+ newEntry.translatorName = wxT("Fayzullin T.N. aka Svobodniy");
+ newEntry.languageFlag = wxT("russia.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_ROMANIAN;
+ newEntry.languageName = wxT("Română");
+ newEntry.languageFile = wxT("romanian.lng");
+ newEntry.translatorName = wxT("Alexandru Bogdan Munteanu");
+ newEntry.languageFlag = wxT("romania.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_SLOVENIAN;
+ newEntry.languageName = wxT("Slovenščina");
+ newEntry.languageFile = wxT("slovenian.lng");
+ newEntry.translatorName = wxT("Matej Badalic");
+ newEntry.languageFlag = wxT("slovakia.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_FINNISH;
+ newEntry.languageName = wxT("Suomi");
+ newEntry.languageFile = wxT("finnish.lng");
+ newEntry.translatorName = wxT("Nalle Juslén");
+ newEntry.languageFlag = wxT("finland.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_SWEDISH;
+ newEntry.languageName = wxT("Svenska");
+ newEntry.languageFile = wxT("swedish.lng");
+ newEntry.translatorName = wxT("Åke Engelbrektson");
+ newEntry.languageFlag = wxT("sweden.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_TURKISH;
+ newEntry.languageName = wxT("Türkçe");
+ newEntry.languageFile = wxT("turkish.lng");
+ newEntry.translatorName = wxT("Kaya Zeren");
+ newEntry.languageFlag = wxT("turkey.png");
+ locMapping.push_back(newEntry);
+
+ // newEntry.languageID = wxLANGUAGE_HEBREW;
+ // newEntry.languageName = wxT("עִבְרִית");
+ // newEntry.languageFile = wxT("hebrew.lng");
+ // newEntry.translatorName = wxT("Moshe Olshevsky");
+ // newEntry.languageFlag = wxT("isreal.png");
+ // locMapping.push_back(newEntry);
+
+ // newEntry.languageID = wxLANGUAGE_ARABIC;
+ // newEntry.languageName = wxT("العربية");
+ // newEntry.languageFile = wxT("arabic.lng");
+ // newEntry.translatorName = wxT("Yousef Shamshoum");
+ // newEntry.languageFlag = wxT("arabic-language.png");
+ // locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_JAPANESE;
+ newEntry.languageName = wxT("日本語");
+ newEntry.languageFile = wxT("japanese.lng");
+ newEntry.translatorName = wxT("Tilt");
+ newEntry.languageFlag = wxT("japan.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_CHINESE_TRADITIONAL;
+ newEntry.languageName = wxT("正體中文");
+ newEntry.languageFile = wxT("chinese_traditional.lng");
+ newEntry.translatorName = wxT("Carlos");
+ newEntry.languageFlag = wxT("taiwan.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_CHINESE_SIMPLIFIED;
+ newEntry.languageName = wxT("简体中文");
+ newEntry.languageFile = wxT("chinese_simple.lng");
+ newEntry.translatorName = wxT("CyberCowBoy");
+ newEntry.languageFlag = wxT("china.png");
+ locMapping.push_back(newEntry);
+
+ newEntry.languageID = wxLANGUAGE_KOREAN;
+ newEntry.languageName = wxT("한국어");
+ newEntry.languageFile = wxT("korean.lng");
+ newEntry.translatorName = wxT("Simon Park");
+ newEntry.languageFlag = wxT("south_korea.png");
+ locMapping.push_back(newEntry);
+}
+
+
+namespace
+{
+int mapLanguageDialect(int language)
+{
+ switch (language) //map language dialects
+ {
+ //variants of wxLANGUAGE_GERMAN
+ case wxLANGUAGE_GERMAN_AUSTRIAN:
+ case wxLANGUAGE_GERMAN_BELGIUM:
+ case wxLANGUAGE_GERMAN_LIECHTENSTEIN:
+ case wxLANGUAGE_GERMAN_LUXEMBOURG:
+ case wxLANGUAGE_GERMAN_SWISS:
+ return wxLANGUAGE_GERMAN;
+
+ //variants of wxLANGUAGE_FRENCH
+ case wxLANGUAGE_FRENCH_BELGIAN:
+ case wxLANGUAGE_FRENCH_CANADIAN:
+ case wxLANGUAGE_FRENCH_LUXEMBOURG:
+ case wxLANGUAGE_FRENCH_MONACO:
+ case wxLANGUAGE_FRENCH_SWISS:
+ return wxLANGUAGE_FRENCH;
+
+ //variants of wxLANGUAGE_DUTCH
+ case wxLANGUAGE_DUTCH_BELGIAN:
+ return wxLANGUAGE_DUTCH;
+
+ //variants of wxLANGUAGE_ITALIAN
+ case wxLANGUAGE_ITALIAN_SWISS:
+ return wxLANGUAGE_ITALIAN;
+
+ //variants of wxLANGUAGE_CHINESE_SIMPLIFIED
+ case wxLANGUAGE_CHINESE:
+ case wxLANGUAGE_CHINESE_SINGAPORE:
+ return wxLANGUAGE_CHINESE_SIMPLIFIED;
+
+ //variants of wxLANGUAGE_CHINESE_TRADITIONAL
+ case wxLANGUAGE_CHINESE_TAIWAN:
+ case wxLANGUAGE_CHINESE_HONGKONG:
+ case wxLANGUAGE_CHINESE_MACAU:
+ return wxLANGUAGE_CHINESE_TRADITIONAL;
+
+ //variants of wxLANGUAGE_RUSSIAN
+ case wxLANGUAGE_RUSSIAN_UKRAINE:
+ return wxLANGUAGE_RUSSIAN;
+
+ //variants of wxLANGUAGE_SPANISH
+ case wxLANGUAGE_SPANISH_ARGENTINA:
+ case wxLANGUAGE_SPANISH_BOLIVIA:
+ case wxLANGUAGE_SPANISH_CHILE:
+ case wxLANGUAGE_SPANISH_COLOMBIA:
+ case wxLANGUAGE_SPANISH_COSTA_RICA:
+ case wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC:
+ case wxLANGUAGE_SPANISH_ECUADOR:
+ case wxLANGUAGE_SPANISH_EL_SALVADOR:
+ case wxLANGUAGE_SPANISH_GUATEMALA:
+ case wxLANGUAGE_SPANISH_HONDURAS:
+ case wxLANGUAGE_SPANISH_MEXICAN:
+ case wxLANGUAGE_SPANISH_MODERN:
+ case wxLANGUAGE_SPANISH_NICARAGUA:
+ case wxLANGUAGE_SPANISH_PANAMA:
+ case wxLANGUAGE_SPANISH_PARAGUAY:
+ case wxLANGUAGE_SPANISH_PERU:
+ case wxLANGUAGE_SPANISH_PUERTO_RICO:
+ case wxLANGUAGE_SPANISH_URUGUAY:
+ case wxLANGUAGE_SPANISH_US:
+ case wxLANGUAGE_SPANISH_VENEZUELA:
+ return wxLANGUAGE_SPANISH;
+
+ //variants of wxLANGUAGE_SWEDISH
+ case wxLANGUAGE_SWEDISH_FINLAND:
+ return wxLANGUAGE_SWEDISH;
+
+ //case wxLANGUAGE_CZECH:
+ //case wxLANGUAGE_FINNISH:
+ //case wxLANGUAGE_GREEK:
+ //case wxLANGUAGE_JAPANESE:
+ //case wxLANGUAGE_POLISH:
+ //case wxLANGUAGE_SLOVENIAN:
+ //case wxLANGUAGE_HUNGARIAN:
+ //case wxLANGUAGE_PORTUGUESE:
+ //case wxLANGUAGE_PORTUGUESE_BRAZILIAN:
+ //case wxLANGUAGE_KOREAN:
+
+ //variants of wxLANGUAGE_ARABIC
+ case wxLANGUAGE_ARABIC_ALGERIA:
+ case wxLANGUAGE_ARABIC_BAHRAIN:
+ case wxLANGUAGE_ARABIC_EGYPT:
+ case wxLANGUAGE_ARABIC_IRAQ:
+ case wxLANGUAGE_ARABIC_JORDAN:
+ case wxLANGUAGE_ARABIC_KUWAIT:
+ case wxLANGUAGE_ARABIC_LEBANON:
+ case wxLANGUAGE_ARABIC_LIBYA:
+ case wxLANGUAGE_ARABIC_MOROCCO:
+ case wxLANGUAGE_ARABIC_OMAN:
+ case wxLANGUAGE_ARABIC_QATAR:
+ case wxLANGUAGE_ARABIC_SAUDI_ARABIA:
+ case wxLANGUAGE_ARABIC_SUDAN:
+ case wxLANGUAGE_ARABIC_SYRIA:
+ case wxLANGUAGE_ARABIC_TUNISIA:
+ case wxLANGUAGE_ARABIC_UAE:
+ case wxLANGUAGE_ARABIC_YEMEN:
+ return wxLANGUAGE_ARABIC;
+
+ //variants of wxLANGUAGE_ENGLISH_UK
+ case wxLANGUAGE_ENGLISH_AUSTRALIA:
+ case wxLANGUAGE_ENGLISH_NEW_ZEALAND:
+ case wxLANGUAGE_ENGLISH_TRINIDAD:
+ case wxLANGUAGE_ENGLISH_CARIBBEAN:
+ case wxLANGUAGE_ENGLISH_JAMAICA:
+ case wxLANGUAGE_ENGLISH_BELIZE:
+ case wxLANGUAGE_ENGLISH_EIRE:
+ case wxLANGUAGE_ENGLISH_SOUTH_AFRICA:
+ case wxLANGUAGE_ENGLISH_ZIMBABWE:
+ case wxLANGUAGE_ENGLISH_BOTSWANA:
+ case wxLANGUAGE_ENGLISH_DENMARK:
+ return wxLANGUAGE_ENGLISH_UK;
+
+ default:
+ return language;
+ }
+}
+
+
+inline
+void exchangeEscapeChars(wxString& data)
+{
+ wxString output;
+
+ const wxChar* input = data.c_str();
+
+ wxChar value;
+ while ((value = *input) != wxChar(0))
+ {
+ //read backslash
+ if (value == wxChar('\\'))
+ {
+ //read next character
+ ++input;
+ if ((value = *input) == wxChar(0))
+ break;
+
+ switch (value)
+ {
+ case wxChar('\\'):
+ output += wxChar('\\');
+ break;
+ case wxChar('n'):
+ output += wxChar('\n');
+ break;
+ case wxChar('t'):
+ output += wxChar('\t');
+ break;
+ case wxChar('\"'):
+ output += wxChar('\"');
+ break;
+ default:
+ output += value;
+ }
+ }
+ else
+ output += value;
+
+ ++input;
+ }
+ data = output;
+}
+
+
+//workaround to get a FILE* from a unicode filename in a portable way
+class UnicodeFileReader
+{
+public:
+ UnicodeFileReader(const wxString& filename) :
+ inputFile(NULL)
+ {
+ wxFFile dummyFile(filename, wxT("rb"));
+ if (dummyFile.IsOpened())
+ {
+ inputFile = dummyFile.fp();
+ dummyFile.Detach();
+ }
+ }
+
+ ~UnicodeFileReader()
+ {
+ if (inputFile != NULL)
+ fclose(inputFile);
+ }
+
+ bool isOkay()
+ {
+ return inputFile != NULL;
+ }
+
+ bool getNextLine(wxString& line)
+ {
+ std::string output;
+
+ while (true)
+ {
+ const int c = fgetc(inputFile);
+ if (c == EOF)
+ return false;
+ else if (c == 0xD)
+ {
+ //Delimiter:
+ //----------
+ //Linux: 0xA \n
+ //Mac: 0xD \r
+ //Win: 0xD 0xA \r\n <- language files are in Windows format
+
+ fgetc(inputFile); //discard the 0xA character
+
+ line = wxString::FromUTF8(output.c_str(), output.length());
+ return true;
+ }
+ output += static_cast<char>(c);
+ }
+ }
+
+private:
+ FILE* inputFile;
+};
+
+
+void loadTranslation(const wxString& filename, Translation& trans) //empty translation on error
+{
+ trans.clear();
+
+ UnicodeFileReader langFile(ffs3::getResourceDir() + wxT("Languages") + ffs3::zToWx(common::FILE_NAME_SEPARATOR) + filename);
+ if (langFile.isOkay())
+ {
+ int rowNumber = 0;
+ wxString original;
+ wxString tmpString;
+ while (langFile.getNextLine(tmpString))
+ {
+ exchangeEscapeChars(tmpString);
+
+ if (rowNumber++ % 2 == 0)
+ original = tmpString;
+ else
+ {
+ const wxString& translation = tmpString;
+
+ if (!original.empty() && !translation.empty())
+ trans.insert(std::make_pair(original, translation));
+ }
+ }
+ }
+}
+}
+
+
+void ffs3::setLanguage(int language)
+{
+ static class StaticInit
+ {
+ public:
+ StaticInit() : loc(wxLANGUAGE_DEFAULT) //wxLocale: we need deferred initialization, sigh...
+ {
+ //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
+ const lconv* localInfo = ::localeconv();
+
+ //actually these two parameters are language dependent, but we take system setting to handle all kinds of language derivations
+ THOUSANDS_SEPARATOR = wxString::FromUTF8(localInfo->thousands_sep);
+ DECIMAL_POINT = wxString::FromUTF8(localInfo->decimal_point);
+
+ // why not working?
+ // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).thousands_sep();
+ // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).decimal_point();
+ }
+ private:
+ wxLocale loc; //required for RTL language support (and nothing else)
+ } dummy;
+
+ activeLanguage = language;
+
+ //default: english
+ wxString languageFile;
+
+ //(try to) retrieve language filename
+ const int mappedLanguage = mapLanguageDialect(language);
+ for (std::vector<LocInfoLine>::const_iterator i = LocalizationInfo::get().begin(); i != LocalizationInfo::get().end(); ++i)
+ if (i->languageID == mappedLanguage)
+ {
+ languageFile = i->languageFile;
+ break;
+ }
+
+ //load language file into buffer
+ activeTranslation.clear();
+ if (!languageFile.empty())
+ {
+ loadTranslation(languageFile, activeTranslation); //empty translation on error
+ if (activeTranslation.empty())
+ {
+ wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + languageFile + wxT("\""), _("Error"), wxOK | wxICON_ERROR);
+ activeLanguage = wxLANGUAGE_ENGLISH; //reset to english language to show this error just once
+ }
+ }
+ else
+ ; //if languageFile is empty texts will be english per default
+}
+
+
+int ffs3::getLanguage()
+{
+ return activeLanguage;
+}
+
+
+int ffs3::retrieveSystemLanguage()
+{
+ return wxLocale::GetSystemLanguage();
+}
+
+
+
+
+/*
+typedef Zbase<wchar_t> Wstring;
+
+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=";
+
+
+template <class T>
+struct Expr
+{
+ typedef T ValueType;
+ virtual ValueType eval() const = 0;
+};
+
+
+template <class In, class Out, class BiOp>
+struct GenericBiExp : public Out
+{
+ 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_;
+};
+
+template <class Out, class In, class BiOp>
+inline
+GenericBiExp<In, Out, BiOp> makeBiExp(const In& lhs, const In& rhs, BiOp biop)
+{
+ return GenericBiExp<In, Out, BiOp>(lhs, rhs, biop);
+}
+
+template <class Out>
+struct TernaryExp : public Out
+{
+ TernaryExp(const Expr<bool>& ifExp, const Out& thenExp, const Out& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) {}
+ virtual typename Out::ValueType eval() const { return ifExp_.eval() ? thenExp_.eval() : elseExp_.eval(); }
+ const Expr<bool>& ifExp_;
+ const Out& thenExp_;
+ const Out& elseExp_;
+};
+
+struct LiteralNumberEx : public Expr<int>
+{
+ LiteralNumberEx(int n) : n_(n) {}
+ virtual ValueType eval() const { return n_; }
+ int n_;
+};
+
+struct NumberN : public Expr<int>
+{
+ NumberN(int& n) : n_(n) {}
+ virtual ValueType eval() const { return n_; }
+ int& n_;
+};
+
+
+class PluralForm
+{
+public:
+ struct ParserError {};
+
+ 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)
+ {
+ 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()));
+ }
+
+ int formCount() const { return count; }
+
+ 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();
+ }
+
+ const Expr<int>& parseNumber(const Wstring& phrase)
+ {
+ Wstring part1, part2;
+
+
+ if (trySplit(phrase, TK_TERNARY_QUEST, part1, part2, TO_LEFT))
+ {
+ 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)));
+ }
+ 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
+ {
+ 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>()));
+ }
+ }
+
+ enum Associativity
+ {
+ TO_LEFT,
+ TO_RIGHT
+ };
+ 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)
+ {
+ dump.push_back(obj);
+ return *boost::any_cast<T>(&dump.back());
+ }
+
+ int count;
+ const Expr<int>* expr;
+ mutable int n_;
+
+ std::list<boost::any> 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);
+*/
+
+
+
+wxString ffs3::translate(const wxString& original) //translate into currently selected language
+{
+ //look for translation in buffer table
+ const Translation::const_iterator i = activeTranslation.find(original);
+ if (i != activeTranslation.end())
+ return i->second.c_str();
+
+ //fallback
+ return original;
+}
diff --git a/shared/pch.h b/shared/pch.h
new file mode 100644
index 00000000..eb95fd50
--- /dev/null
+++ b/shared/pch.h
@@ -0,0 +1,119 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+#ifndef FFS_PRECOMPILED_HEADER
+#define FFS_PRECOMPILED_HEADER
+
+//pay attention when using this file: might cause issues!
+#ifdef NDEBUG
+#error do NOT use in release build!
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable:4996) //"warning C4996: 'std::copy': Function call with parameters that may be unsafe"
+#endif
+
+//#####################################################
+// basic wxWidgets headers
+#ifndef WX_PRECOMP
+#define WX_PRECOMP
+#endif
+
+#include <wx/wxprec.h>
+
+//#####################################################
+// #include other rarely changing headers here
+
+//STL headers
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+#include <queue>
+#include <deque>
+#include <stack>
+#include <list>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <numeric>
+#include <memory>
+#include <utility>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <new>
+#include <stdexcept>
+
+//other wxWidgets headers
+#include <wx/log.h>
+#include <wx/grid.h>
+#include <wx/animate.h>
+#include <wx/app.h>
+#include <wx/arrstr.h>
+#include <wx/bitmap.h>
+#include <wx/bmpbuttn.h>
+#include <wx/button.h>
+#include <wx/checkbox.h>
+#include <wx/choice.h>
+#include <wx/clipbrd.h>
+#include <wx/cmdline.h>
+#include <wx/colour.h>
+#include <wx/config.h>
+#include <wx/dc.h>
+#include <wx/dialog.h>
+#include <wx/dir.h>
+#include <wx/dnd.h>
+#include <wx/file.h>
+#include <wx/filename.h>
+#include <wx/filepicker.h>
+#include <wx/font.h>
+#include <wx/frame.h>
+#include <wx/gauge.h>
+#include <wx/gdicmn.h>
+#include <wx/grid.h>
+#include <wx/hyperlink.h>
+#include <wx/icon.h>
+#include <wx/image.h>
+#include <wx/intl.h>
+#include <wx/log.h>
+#include <wx/menu.h>
+#include <wx/msgdlg.h>
+#include <wx/panel.h>
+#include <wx/radiobut.h>
+#include <wx/settings.h>
+#include <wx/sizer.h>
+#include <wx/statbmp.h>
+#include <wx/statbox.h>
+#include <wx/statline.h>
+#include <wx/stattext.h>
+#include <wx/stdpaths.h>
+#include <wx/stopwatch.h>
+#include <wx/stream.h>
+#include <wx/string.h>
+#include <wx/textctrl.h>
+#include <wx/thread.h>
+#include <wx/utils.h>
+#include <wx/wfstream.h>
+#include <wx/zipstrm.h>
+#include <wx/scrolwin.h>
+#include <wx/notebook.h>
+#include <wx/help.h>
+#include <wx/event.h>
+
+//other
+#include "../shared/tinyxml/tinyxml.h"
+#include <sys/stat.h>
+
+//Boost
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_array.hpp>
+
+#ifdef __WXMSW__
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#endif //__WXMSW__
+
+#endif //FFS_PRECOMPILED_HEADER
diff --git a/shared/perf.h b/shared/perf.h
index 1ad4650d..641eee2b 100644
--- a/shared/perf.h
+++ b/shared/perf.h
@@ -8,13 +8,15 @@
#define DEBUG_PERF_HEADER
#include <sstream>
+
+#ifdef __WXWINDOWS__
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#else
//#define WIN32_LEAN_AND_MEAN -> not in a header
-/*
#include <windows.h>
#undef max
#undef min
-*/
-#include <wx/msw/wrapwin.h> //includes "windows.h"
+#endif
#ifdef __MINGW32__
@@ -28,37 +30,48 @@ class CpuTimer
public:
class TimerError {};
- DEPRECATED(CpuTimer()) : resultWasShown(false), startTime(), frequency()
+ DEPRECATED(CpuTimer()) : frequency(), startTime(), resultShown(false)
{
+ SetThreadAffinity dummy;
if (!::QueryPerformanceFrequency(&frequency)) throw TimerError();
if (!::QueryPerformanceCounter (&startTime)) throw TimerError();
}
~CpuTimer()
{
- if (!resultWasShown)
+ if (!resultShown)
showResult();
}
void showResult()
{
+ SetThreadAffinity dummy;
LARGE_INTEGER currentTime = {};
if (!::QueryPerformanceCounter(&currentTime)) throw TimerError();
- const long delta = 1000.0 * (currentTime.QuadPart - startTime.QuadPart) / frequency.QuadPart;
+ const long delta = static_cast<long>(1000.0 * (currentTime.QuadPart - startTime.QuadPart) / frequency.QuadPart);
std::ostringstream ss;
ss << delta << " ms";
::MessageBoxA(NULL, ss.str().c_str(), "Timer", 0);
- resultWasShown = true;
+ resultShown = true;
if (!::QueryPerformanceCounter(&startTime)) throw TimerError(); //don't include call to MessageBox()!
}
private:
- bool resultWasShown;
- LARGE_INTEGER startTime;
+ class SetThreadAffinity
+ {
+ public:
+ SetThreadAffinity() : oldmask(::SetThreadAffinityMask(::GetCurrentThread(), 1)) { if (oldmask == 0) throw TimerError(); }
+ ~SetThreadAffinity() { ::SetThreadAffinityMask(::GetCurrentThread(), oldmask); }
+ private:
+ const DWORD_PTR oldmask;
+ };
+
LARGE_INTEGER frequency;
+ LARGE_INTEGER startTime;
+ bool resultShown;
};
//two macros for quick performance measurements
diff --git a/shared/privilege.cpp b/shared/privilege.cpp
index 00eb2855..5cb664e1 100644
--- a/shared/privilege.cpp
+++ b/shared/privilege.cpp
@@ -1,7 +1,6 @@
#include "privilege.h"
-#include <wx/intl.h>
#include "system_func.h"
-//#include <boost/shared_ptr.hpp>
+#include "i18n.h"
#include "loki/ScopeGuard.h"
using namespace ffs3;
@@ -14,7 +13,7 @@ Privileges& Privileges::getInstance()
}
-bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw FileError()
+bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw (FileError)
{
HANDLE hToken = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
@@ -58,7 +57,7 @@ bool Privileges::privilegeIsActive(LPCTSTR privilege) //throw FileError()
}
-void Privileges::setPrivilege(LPCTSTR privilege, bool enable) //throw FileError()
+void Privileges::setPrivilege(LPCTSTR privilege, bool enable) //throw (FileError)
{
HANDLE hToken = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle,
diff --git a/shared/privilege.h b/shared/privilege.h
index c5ec99c7..0c5d5f0c 100644
--- a/shared/privilege.h
+++ b/shared/privilege.h
@@ -15,7 +15,7 @@ class Privileges
public:
static Privileges& getInstance();
- void ensureActive(LPCTSTR privilege) //throw FileError()
+ void ensureActive(LPCTSTR privilege) //throw (FileError)
{
if (activePrivileges.find(privilege) != activePrivileges.end())
return; //privilege already active
@@ -45,8 +45,8 @@ private:
catch(...) {}
}
- static bool privilegeIsActive(LPCTSTR privilege); //throw FileError()
- static void setPrivilege(LPCTSTR privilege, bool enable); //throw FileError()
+ static bool privilegeIsActive(LPCTSTR privilege); //throw (FileError)
+ static void setPrivilege(LPCTSTR privilege, bool enable); //throw (FileError)
typedef std::map<Zstring, bool> PrivBuffType; //bool: enabled by this application
diff --git a/shared/recycler.cpp b/shared/recycler.cpp
index 4e9ce9a3..a2b080c9 100644
--- a/shared/recycler.cpp
+++ b/shared/recycler.cpp
@@ -5,10 +5,10 @@
// **************************************************************************
//
#include "recycler.h"
-#include "string_conv.h"
-#include <wx/intl.h>
#include <stdexcept>
#include <iterator>
+#include "i18n.h"
+#include "string_conv.h"
#ifdef FFS_WIN
#include "dll_loader.h"
diff --git a/shared/resolve_path.cpp b/shared/resolve_path.cpp
new file mode 100644
index 00000000..b17c3fb5
--- /dev/null
+++ b/shared/resolve_path.cpp
@@ -0,0 +1,319 @@
+#include "resolve_path.h"
+#include <boost/scoped_array.hpp>
+#include <wx/utils.h>
+#include <wx/datetime.h>
+#include "string_conv.h"
+#include "system_constants.h"
+#include "loki/ScopeGuard.h"
+
+#ifdef FFS_WIN
+#include "dll_loader.h"
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "long_path_prefix.h"
+
+#elif defined FFS_LINUX
+#include <map>
+#include "file_traverser.h"
+#include "stdlib.h"
+#endif
+
+using namespace ffs3;
+using namespace common;
+
+
+namespace
+{
+#ifdef FFS_WIN
+Zstring resolveRelativePath(const Zstring& relativeName, DWORD proposedBufferSize = 1000)
+{
+ boost::scoped_array<Zchar> fullPath(new Zchar[proposedBufferSize]);
+ const DWORD rv = ::GetFullPathName(
+ applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName,
+ proposedBufferSize, //__in DWORD nBufferLength,
+ fullPath.get(), //__out LPTSTR lpBuffer,
+ NULL); //__out LPTSTR *lpFilePart
+ if (rv == 0 || rv == proposedBufferSize)
+ //ERROR! Don't do anything
+ return relativeName;
+ if (rv > proposedBufferSize)
+ return resolveRelativePath(relativeName, rv);
+
+ return fullPath.get();
+}
+
+#elif defined FFS_LINUX
+Zstring resolveRelativePath(const Zstring& relativeName) //additional: resolves symbolic links!!!
+{
+ char absolutePath[PATH_MAX + 1];
+ if (::realpath(relativeName.c_str(), absolutePath) == NULL)
+ //ERROR! Don't do anything
+ return relativeName;
+
+ return Zstring(absolutePath);
+}
+#endif
+
+
+bool replaceMacro(wxString& macro) //macro without %-characters, return true if replaced successfully
+{
+ if (macro.IsEmpty())
+ return false;
+
+ //there are equally named environment variables %TIME%, %DATE% existing, so replace these first!
+ if (macro.CmpNoCase(wxT("time")) == 0)
+ {
+ macro = wxDateTime::Now().FormatISOTime();
+ macro.Replace(wxT(":"), wxT(""));
+ return true;
+ }
+
+ if (macro.CmpNoCase(wxT("date")) == 0)
+ {
+ macro = wxDateTime::Now().FormatISODate();
+ return true;
+ }
+
+ if (macro.CmpNoCase(wxT("weekday")) == 0)
+ {
+ macro = wxDateTime::Now().Format(wxT("%A"));
+ return true;
+ }
+
+ if (macro.CmpNoCase(wxT("month")) == 0)
+ {
+ macro = wxDateTime::Now().Format(wxT("%B"));
+ return true;
+ }
+
+ if (macro.CmpNoCase(wxT("week")) == 0)
+ {
+ macro = wxDateTime::Now().Format(wxT("%U"));
+ return true;
+ }
+
+ if (macro.CmpNoCase(wxT("year")) == 0)
+ {
+ macro = wxDateTime::Now().Format(wxT("%Y"));
+ return true;
+ }
+
+ //try to apply environment variables
+ wxString envValue;
+ if (wxGetEnv(macro, &envValue))
+ {
+ macro = envValue;
+
+ //some postprocessing:
+ macro.Trim(true); //remove leading, trailing blanks
+ macro.Trim(false); //
+
+ //remove leading, trailing double-quotes
+ if (macro.StartsWith(wxT("\"")) &&
+ macro.EndsWith(wxT("\"")) &&
+ macro.length() >= 2)
+ macro = wxString(macro.c_str() + 1, macro.length() - 2);
+ return true;
+ }
+
+ return false;
+}
+
+
+void expandMacros(wxString& text)
+{
+ const wxChar SEPARATOR = '%';
+
+ if (text.Find(SEPARATOR) != wxNOT_FOUND)
+ {
+ wxString prefix = text.BeforeFirst(SEPARATOR);
+ wxString postfix = text.AfterFirst(SEPARATOR);
+ if (postfix.Find(SEPARATOR) != wxNOT_FOUND)
+ {
+ wxString potentialMacro = postfix.BeforeFirst(SEPARATOR);
+ wxString rest = postfix.AfterFirst(SEPARATOR); //text == prefix + SEPARATOR + potentialMacro + SEPARATOR + rest
+
+ if (replaceMacro(potentialMacro))
+ {
+ expandMacros(rest);
+ text = prefix + potentialMacro + rest;
+ }
+ else
+ {
+ rest = SEPARATOR + rest;
+ expandMacros(rest);
+ text = prefix + SEPARATOR + potentialMacro + rest;
+ }
+ }
+ }
+}
+
+
+#ifdef FFS_LINUX
+class TraverseMedia : public ffs3::TraverseCallback
+{
+public:
+ typedef std::map<Zstring, Zstring> DeviceList; //device name -> device path mapping
+
+ TraverseMedia(DeviceList& devices) : devices_(devices) {}
+
+ virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {}
+ virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) {}
+ virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName)
+ {
+ devices_.insert(std::make_pair(shortName, fullName));
+ return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //DON'T traverse into subdirs
+ }
+ virtual void onError(const wxString& errorText) {}
+
+private:
+ DeviceList& devices_;
+};
+#endif
+
+
+Zstring getVolumePath(const Zstring& volumeName) //empty string on error
+{
+#ifdef FFS_WIN
+ const size_t volGuidSize = 10000;
+ boost::scoped_array<wchar_t> volGuid(new wchar_t[volGuidSize]);
+
+ HANDLE hVol = ::FindFirstVolume(volGuid.get(), volGuidSize);
+ if (hVol != INVALID_HANDLE_VALUE)
+ {
+ Loki::ScopeGuard dummy = Loki::MakeGuard(::FindVolumeClose, hVol);
+ (void)dummy;
+
+ do
+ {
+ const size_t volNameSize = MAX_PATH + 1;
+ boost::scoped_array<wchar_t> volName(new wchar_t[volNameSize]);
+
+ if (::GetVolumeInformation(volGuid.get(), //__in_opt LPCTSTR lpRootPathName,
+ volName.get(), //__out LPTSTR lpVolumeNameBuffer,
+ volNameSize, //__in DWORD nVolumeNameSize,
+ NULL, //__out_opt LPDWORD lpVolumeSerialNumber,
+ NULL, //__out_opt LPDWORD lpMaximumComponentLength,
+ NULL, //__out_opt LPDWORD lpFileSystemFlags,
+ NULL, //__out LPTSTR lpFileSystemNameBuffer,
+ 0)) //__in DWORD nFileSystemNameSize
+ {
+ if (EqualFilename()(volumeName, Zstring(volName.get())))
+ {
+ //GetVolumePathNamesForVolumeName is not available for Windows 2000!
+ typedef BOOL (WINAPI *GetVolumePathNamesForVolumeNameWFunc)(LPCWSTR lpszVolumeName,
+ LPWCH lpszVolumePathNames,
+ DWORD cchBufferLength,
+ PDWORD lpcchReturnLength);
+
+ static const GetVolumePathNamesForVolumeNameWFunc getVolumePathNamesForVolumeName =
+ util::getDllFun<GetVolumePathNamesForVolumeNameWFunc>(L"kernel32.dll", "GetVolumePathNamesForVolumeNameW");
+
+ if (getVolumePathNamesForVolumeName != NULL)
+ {
+ const DWORD volPathSize = 10000;
+ boost::scoped_array<wchar_t> volPath(new wchar_t[volPathSize]);
+
+ DWORD returnedLen = 0;
+ if (getVolumePathNamesForVolumeName(volGuid.get(), //__in LPCTSTR lpszVolumeName,
+ volPath.get(), //__out LPTSTR lpszVolumePathNames,
+ volPathSize, //__in DWORD cchBufferLength,
+ &returnedLen)) //__out PDWORD lpcchReturnLength
+ {
+ return volPath.get(); //return first path name in double-null terminated list!
+ }
+ }
+ return volGuid.get(); //GUID looks ugly, but should be working correctly
+ }
+ }
+ }
+ while (::FindNextVolume(hVol, volGuid.get(), volGuidSize));
+ }
+
+#elif defined FFS_LINUX
+ //due to the naming convention on Linux /media/<volume name> this function is not that useful, but...
+
+ TraverseMedia::DeviceList deviceList;
+
+ TraverseMedia traverser(deviceList);
+ traverseFolder("/media", false, traverser); //traverse one level
+
+ TraverseMedia::DeviceList::const_iterator iter = deviceList.find(volumeName);
+ if (iter != deviceList.end())
+ return iter->second;
+#endif
+ return Zstring();
+}
+
+
+void expandVolumeName(Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder
+{
+ Zstring before;
+ Zstring volname;
+ Zstring after;
+
+ size_t posStart = text.find(Zstr("["));
+ if (posStart != Zstring::npos)
+ {
+ size_t posEnd = text.find(Zstr("]"), posStart);
+ if (posEnd != Zstring::npos)
+ {
+ before = Zstring(text.c_str(), posStart);
+ volname = Zstring(text.c_str() + posStart + 1, posEnd - posStart - 1);
+ after = Zstring(text.c_str() + posEnd + 1);
+
+ if (after.StartsWith(Zstr(":")))
+ after = after.AfterFirst(Zstr(':'));
+ if (after.StartsWith(Zstring() + FILE_NAME_SEPARATOR))
+ after = after.AfterFirst(FILE_NAME_SEPARATOR);
+ }
+ }
+
+ if (volname.empty())
+ return;
+
+ Zstring volPath = getVolumePath(volname); //return empty string on error
+ if (volPath.empty())
+ return;
+
+ if (!volPath.EndsWith(FILE_NAME_SEPARATOR))
+ volPath += FILE_NAME_SEPARATOR;
+
+ text = before + volPath + after;
+}
+}
+
+
+Zstring ffs3::getFormattedDirectoryName(const Zstring& dirname)
+{
+ //Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names.
+ //note: don't combine directory formatting with wxFileName, as it doesn't respect //?/ - prefix!
+
+ wxString dirnameTmp = zToWx(dirname);
+ expandMacros(dirnameTmp);
+
+ Zstring output = wxToZ(dirnameTmp);
+
+ expandVolumeName(output);
+
+ output.Trim();
+
+ if (output.empty()) //an empty string will later be returned as "\"; this is not desired
+ return Zstring();
+
+ /*
+ resolve relative names; required by:
+ WINDOWS:
+ - \\?\-prefix which needs absolute names
+ - Volume Shadow Copy: volume name needs to be part of each filename
+ - file icon buffer (at least for extensions that are actually read from disk, e.g. "exe")
+ - ::SHFileOperation(): Using relative path names is not thread safe
+ WINDOWS/LINUX:
+ - detection of dependent directories, e.g. "\" and "C:\test"
+ */
+ output = resolveRelativePath(output);
+
+ if (!output.EndsWith(FILE_NAME_SEPARATOR))
+ output += FILE_NAME_SEPARATOR;
+
+ return output;
+}
diff --git a/shared/resolve_path.h b/shared/resolve_path.h
new file mode 100644
index 00000000..0bd4b0d2
--- /dev/null
+++ b/shared/resolve_path.h
@@ -0,0 +1,19 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+#ifndef RESOLVE_PATH_H_INCLUDED
+#define RESOLVE_PATH_H_INCLUDED
+
+#include "zstring.h"
+
+
+namespace ffs3
+{
+Zstring getFormattedDirectoryName(const Zstring& dirname);
+}
+
+
+#endif // RESOLVE_PATH_H_INCLUDED
diff --git a/shared/serialize.cpp b/shared/serialize.cpp
index f71207be..719d0861 100644
--- a/shared/serialize.cpp
+++ b/shared/serialize.cpp
@@ -5,12 +5,12 @@
// **************************************************************************
//
#include "serialize.h"
-#include <wx/intl.h>
+#include "i18n.h"
using namespace util;
-void ReadInputStream::throwReadError() const //throw FileError()
+void ReadInputStream::throwReadError() const //throw (FileError)
{
throw ffs3::FileError(wxString(_("Error reading from synchronization database:")) + wxT(" \n") +
wxT("\"") + errorObjName_ + wxT("\""));
@@ -33,7 +33,7 @@ ReadInputStream::CharArray ReadInputStream::readArrayC() const
//--------------------------------------------------------------------------------------------------------
-void WriteOutputStream::throwWriteError() const //throw FileError()
+void WriteOutputStream::throwWriteError() const //throw (FileError)
{
throw ffs3::FileError(wxString(_("Error writing to synchronization database:")) + wxT(" \n") +
wxT("\"") + errorObjName_ + wxT("\""));
diff --git a/shared/serialize.h b/shared/serialize.h
index 8da0a449..db1848d5 100644
--- a/shared/serialize.h
+++ b/shared/serialize.h
@@ -27,18 +27,18 @@ Zstring readString(wxInputStream& stream);
void writeString(wxOutputStream& stream, const Zstring& str);
-class ReadInputStream //throw FileError()
+class ReadInputStream //throw (FileError)
{
protected:
ReadInputStream(wxInputStream& stream, const wxString& errorObjName) : stream_(stream), errorObjName_(errorObjName) {}
template <class T>
- T readNumberC() const; //throw FileError(), checked read operation
+ T readNumberC() const; //throw (FileError), checked read operation
- Zstring readStringC() const; //throw FileError(), checked read operation
+ Zstring readStringC() const; //throw (FileError), checked read operation
typedef boost::shared_ptr<std::vector<char> > CharArray; //there's no guarantee std::string has a ref-counted implementation... so use this "thing"
- CharArray readArrayC() const; //throw FileError()
+ CharArray readArrayC() const; //throw (FileError)
void check() const;
@@ -49,22 +49,22 @@ protected:
private:
wxInputStream& stream_;
- void throwReadError() const; //throw FileError()
+ void throwReadError() const; //throw (FileError)
const wxString& errorObjName_; //used for error text only
};
-class WriteOutputStream //throw FileError()
+class WriteOutputStream //throw (FileError)
{
protected:
WriteOutputStream(const wxString& errorObjName, wxOutputStream& stream) : stream_(stream), errorObjName_(errorObjName) {}
template <class T>
- void writeNumberC(T number) const; //throw FileError(), checked write operation
+ void writeNumberC(T number) const; //throw (FileError), checked write operation
- void writeStringC(const Zstring& str) const; //throw FileError(), checked write operation
+ void writeStringC(const Zstring& str) const; //throw (FileError), checked write operation
- void writeArrayC(const std::vector<char>& buffer) const; //throw FileError()
+ void writeArrayC(const std::vector<char>& buffer) const; //throw (FileError)
void check() const;
@@ -75,7 +75,7 @@ protected:
private:
wxOutputStream& stream_;
- void throwWriteError() const; //throw FileError()
+ void throwWriteError() const; //throw (FileError)
const wxString& errorObjName_; //used for error text only!
};
diff --git a/shared/shadow.cpp b/shared/shadow.cpp
index 9dab1494..150a8bbf 100644
--- a/shared/shadow.cpp
+++ b/shared/shadow.cpp
@@ -6,8 +6,8 @@
//
#include "shadow.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
-#include <wx/intl.h>
#include "system_constants.h"
+#include "i18n.h"
#include "dll_loader.h"
#include <stdexcept>
#include "assert_static.h"
diff --git a/shared/standard_paths.cpp b/shared/standard_paths.cpp
index 6f723ef7..e4f87dd6 100644
--- a/shared/standard_paths.cpp
+++ b/shared/standard_paths.cpp
@@ -6,12 +6,12 @@
//
#include "standard_paths.h"
#include <wx/stdpaths.h>
-#include <wx/filename.h>
#include "system_constants.h"
#include "string_conv.h"
using namespace ffs3;
+
bool ffs3::isPortableVersion()
{
#ifdef FFS_WIN
@@ -25,7 +25,7 @@ bool ffs3::isPortableVersion()
const wxString& ffs3::getBinaryDir()
{
- static wxString instance = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath() + zToWx(common::FILE_NAME_SEPARATOR);
+ static wxString instance = zToWx(wxToZ(wxStandardPaths::Get().GetExecutablePath()).BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR);
return instance;
}
diff --git a/shared/string_conv.h b/shared/string_conv.h
index 85181d11..24cb521e 100644
--- a/shared/string_conv.h
+++ b/shared/string_conv.h
@@ -22,8 +22,8 @@ Zstring wxToZ(const wxChar* str);
Zstring wxToZ(wxChar ch);
-
-
+wxString zxToWx(const zxString& str);
+zxString wxToZx(const wxString& str);
@@ -105,6 +105,20 @@ Zstring wxToZ(wxChar ch)
{
return wxToZ(wxString(ch));
}
+
+
+inline
+wxString zxToWx(const zxString& str)
+{
+ return wxString(str.c_str(), str.length());
+}
+
+
+inline
+zxString wxToZx(const wxString& str)
+{
+ return zxString(str.c_str(), str.length());
+}
}
#endif // STRINGCONV_H_INCLUDED
diff --git a/shared/symlink_target.h b/shared/symlink_target.h
index 6dec85a0..200c76d8 100644
--- a/shared/symlink_target.h
+++ b/shared/symlink_target.h
@@ -10,9 +10,9 @@
#include "loki/ScopeGuard.h"
#include <boost/scoped_array.hpp>
#include "system_func.h"
-#include <wx/intl.h>
#include "string_conv.h"
#include "file_error.h"
+#include "i18n.h"
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
@@ -72,7 +72,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw (FileError)
try //reading certain symlinks requires admin rights! This shall not cause an error in user mode!
{
//allow access to certain symbolic links/junctions
- ffs3::Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw FileError()
+ ffs3::Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw (FileError)
}
catch (...) {}
diff --git a/shared/system_constants.h b/shared/system_constants.h
index 6f89609e..9c0cd4a4 100644
--- a/shared/system_constants.h
+++ b/shared/system_constants.h
@@ -16,7 +16,7 @@ namespace common
// GLOBALS
//------------------------------------------------
#ifdef FFS_WIN
-const Zchar FILE_NAME_SEPARATOR = '\\';
+const Zchar FILE_NAME_SEPARATOR = '\\'; //
const wxChar LINE_BREAK[] = wxT("\r\n"); //internal linkage
#elif defined FFS_LINUX
const Zchar FILE_NAME_SEPARATOR = '/';
@@ -26,5 +26,4 @@ const wxChar LINE_BREAK[] = wxT("\n");
const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF";
}
-
#endif // SYSTEMCONSTANTS_H_INCLUDED
diff --git a/shared/util.cpp b/shared/util.cpp
index 2af7dc5b..7ab5b4e7 100644
--- a/shared/util.cpp
+++ b/shared/util.cpp
@@ -9,7 +9,7 @@
#include <wx/textctrl.h>
#include <wx/combobox.h>
#include <wx/filepicker.h>
-#include "localization.h"
+#include "i18n.h"
#include "file_handling.h"
#include "string_conv.h"
#include <stdexcept>
@@ -157,7 +157,7 @@ wxString ffs3::utcTimeToLocalString(const wxLongLong& utcTime)
fileTimeLong += wxLongLong(2, 3054539008UL); //timeshift between ansi C time and FILETIME in seconds == 11644473600s
fileTimeLong *= 10000000;
- FILETIME lastWriteTimeUtc;
+ FILETIME lastWriteTimeUtc = {};
lastWriteTimeUtc.dwLowDateTime = fileTimeLong.GetLo(); //GetLo() returns unsigned
lastWriteTimeUtc.dwHighDateTime = static_cast<DWORD>(fileTimeLong.GetHi()); //GetHi() returns signed
@@ -212,25 +212,6 @@ wxString ffs3::utcTimeToLocalString(const wxLongLong& utcTime)
wxT("\n\n") + getLastErrorFormatted()).ToAscii()));
}
- /*
- //assemble time string (performance optimized) -> note: performance argument is not valid anymore
-
- wxString formattedTime;
- formattedTime.reserve(20);
-
- writeFourDigitNumber(time.wYear, formattedTime);
- formattedTime += wxChar('-');
- writeTwoDigitNumber(time.wMonth, formattedTime);
- formattedTime += wxChar('-');
- writeTwoDigitNumber(time.wDay, formattedTime);
- formattedTime += wxChar(' ');
- formattedTime += wxChar(' ');
- writeTwoDigitNumber(time.wHour, formattedTime);
- formattedTime += wxChar(':');
- writeTwoDigitNumber(time.wMinute, formattedTime);
- formattedTime += wxChar(':');
- writeTwoDigitNumber(time.wSecond, formattedTime);
- */
const wxDateTime localTime(systemTimeLocal.wDay,
wxDateTime::Month(systemTimeLocal.wMonth - 1),
systemTimeLocal.wYear,
diff --git a/shared/xml_base.cpp b/shared/xml_base.cpp
index c3d04364..3f2a9e48 100644
--- a/shared/xml_base.cpp
+++ b/shared/xml_base.cpp
@@ -5,8 +5,8 @@
// **************************************************************************
//
#include "xml_base.h"
-#include <wx/intl.h>
#include "file_io.h"
+#include "i18n.h"
#include "string_conv.h"
#include "system_constants.h"
#include <boost/scoped_array.hpp>
@@ -68,8 +68,8 @@ void loadRawXmlDocument(const wxString& filename, TiXmlDocument& document) //thr
try
{
- FileInput inputFile(wxToZ(filename)); //throw FileError();
- const size_t bytesRead = inputFile.read(&inputBuffer[0], inputBuffer.size()); //throw FileError()
+ FileInput inputFile(wxToZ(filename)); //throw (FileError);
+ const size_t bytesRead = inputFile.read(&inputBuffer[0], inputBuffer.size()); //throw (FileError)
if (bytesRead == 0 || bytesRead >= inputBuffer.size()) //treat XML files larger than 2 MB as erroneous: loading larger files just wastes CPU + memory
throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\""));
@@ -165,14 +165,14 @@ bool saveNecessary(const Zstring& filename, const std::string& dataToWrite) //th
{
try
{
- if (ffs3::getFilesize(filename) != static_cast<unsigned long>(dataToWrite.size())) //throw FileError();
+ if (ffs3::getFilesize(filename) != static_cast<unsigned long>(dataToWrite.size())) //throw (FileError);
return true;
boost::scoped_array<char> inputBuffer(new char[dataToWrite.size() + 1]); //+ 1 in order to test for end of file!
- FileInput inputFile(filename); //throw FileError();
+ FileInput inputFile(filename); //throw (FileError);
- const size_t bytesRead = inputFile.read(inputBuffer.get(), dataToWrite.size() + 1); //throw FileError()
+ const size_t bytesRead = inputFile.read(inputBuffer.get(), dataToWrite.size() + 1); //throw (FileError)
if (bytesRead != dataToWrite.size()) //implicit test for eof!
return true;
@@ -200,7 +200,7 @@ void xmlAccess::saveXmlDocument(const wxString& filename, const TiXmlDocument& d
{
try
{
- FileOutput outputFile(wxToZ(filename)); //throw FileError()
+ FileOutput outputFile(wxToZ(filename), FileOutput::ACC_OVERWRITE); //throw (FileError)
outputFile.write(buffer.c_str(), buffer.length()); //
}
catch (const FileError& error) //more detailed error messages than with wxWidgets
diff --git a/shared/zbase.h b/shared/zbase.h
index a0322de0..f08a87e3 100644
--- a/shared/zbase.h
+++ b/shared/zbase.h
@@ -125,7 +125,7 @@ private:
static size_t calcCapacity(size_t length)
{
- return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
+ return std::max<size_t>(16, length + length / 2); //exponential growth + min size
}
};
@@ -207,7 +207,7 @@ private:
static size_t calcCapacity(size_t length)
{
- return (length + (19 - length % 16)); //allocate some additional length to speed up concatenation
+ return std::max<size_t>(16, length + length / 2); //exponential growth + min size
}
};
@@ -228,6 +228,11 @@ public:
operator const T* () const; //implicit conversion to C-string
//STL accessors
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
const T* begin() const;
const T* end() const;
T* begin();
@@ -262,11 +267,13 @@ public:
size_t find(const T* str, size_t pos = 0) const; //returns "npos" if not found
size_t find(T ch, size_t pos = 0) const; //
size_t rfind(T ch, size_t pos = npos) const; //
+ size_t rfind(const T* str, size_t pos = npos) const; //
Zbase& replace(size_t pos1, size_t n1, const T* str, size_t n2);
void reserve(size_t minCapacity);
Zbase& assign(const T* source, size_t len);
void resize(size_t newSize, T fillChar = 0);
void swap(Zbase& other);
+ void push_back(T val); //STL access
//number conversion
template <class N> static Zbase fromNumber(N number);
@@ -620,6 +627,21 @@ size_t Zbase<T, SP, AP>::rfind(T ch, size_t pos) const
template <class T, template <class, class> class SP, class AP>
+inline
+size_t Zbase<T, SP, AP>::rfind(const T* str, size_t pos) const
+{
+ assert(pos == npos || pos <= length());
+
+ const size_t strLen = z_impl::cStringLength(str);
+ const T* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length());
+
+ const T* iter = std::find_end(begin(), currEnd,
+ str, str + strLen);
+ return iter == currEnd ? npos : iter - begin();
+}
+
+
+template <class T, template <class, class> class SP, class AP>
Zbase<T, SP, AP>& Zbase<T, SP, AP>::replace(size_t pos1, size_t n1, const T* str, size_t n2)
{
assert(str < rawStr || rawStr + length() < str); //str mustn't point to data in this string
@@ -863,6 +885,14 @@ T* Zbase<T, SP, AP>::end()
template <class T, template <class, class> class SP, class AP>
inline
+void Zbase<T, SP, AP>::push_back(T val)
+{
+ operator+=(val);
+}
+
+
+template <class T, template <class, class> class SP, class AP>
+inline
bool Zbase<T, SP, AP>::empty() const
{
return length() == 0;
@@ -1067,8 +1097,8 @@ template <class N>
inline
N Zbase<T, SP, AP>::toNumber() const
{
- std::basic_istringstream<T> ss(std::basic_string<T>(rawStr));
- T number = 0;
+ std::basic_istringstream<T> ss((std::basic_string<T>(rawStr)));
+ N number = 0;
ss >> number;
return number;
}
diff --git a/shared/zstring.h b/shared/zstring.h
index 73e2f066..286ed7cc 100644
--- a/shared/zstring.h
+++ b/shared/zstring.h
@@ -115,10 +115,10 @@ typedef char Zchar;
#endif
//"The reason for all the fuss above" (Loki/SmartPtr)
-typedef Zbase<Zchar, StorageRefCount, AllocatorFreeStoreChecked> Zstring;
-
-
+typedef Zbase<Zchar, StorageRefCount, AllocatorFreeStoreChecked> Zstring; //for use with file names
+//fast replacement for wxString modelling exponential growth
+typedef Zbase<wchar_t, StorageRefCount, AllocatorFreeStoreChecked> zxString; //general unicode string
bgstack15