Change backup policy

1 - Reports the error to the user in a popup.
2 - Names of the backups include the timstamp instead of a number. Names have more sense and the files can be sorted by names what is also the age order.
3 - Behaviors when changing configuration improved, the number of files present on the system was not reduced when reducing the number of backups or backups deactivated.
This commit is contained in:
plgarcia
2017-12-07 13:20:32 +01:00
committed by wmayer
parent 2336d1be8d
commit 59269207b4
4 changed files with 344 additions and 17 deletions

View File

@@ -2263,6 +2263,8 @@ public:
BackupPolicy() {
policy = Standard;
numberOfFiles = 1;
useFCBakExtension = false;
saveBackupDateFormat = "%Y%m%d-%H%M%S";
}
~BackupPolicy() {
}
@@ -2272,6 +2274,12 @@ public:
void setNumberOfFiles(int count) {
numberOfFiles = count;
}
void useBackupExtension(bool on) {
useFCBakExtension = on;
}
void setDateFormat(const std::string& fmt) {
saveBackupDateFormat = fmt;
}
void apply(const std::string& sourcename, const std::string& targetname) {
switch (policy) {
case Standard:
@@ -2341,16 +2349,191 @@ private:
Base::Console().Warning("Cannot rename file from '%s' to '%s'\n",
sourcename.c_str(), targetname.c_str());
}
}
void applyTimeStamp(const std::string& sourcename, const std::string& targetname) {
(void)sourcename;
(void)targetname;
Base::FileInfo fi(targetname);
std::string fn = sourcename;
std::string ext = fi.extension();
std::string bn; // full path with no extension but with "."
std::string pbn; // base name of the project + "."
if (!ext.empty()) {
bn = fi.filePath().substr(0, fi.filePath().length() - ext.length());
pbn = fi.fileName().substr(0, fi.fileName().length() - ext.length());
}
else {
bn = fi.filePath() + ".";
pbn = fi.fileName() + ".";
}
bool backupManagementError = false; // Note error and report at the end
if (fi.exists()) {
// replace . by - in format to avoid . between base name and extension
boost::replace_all(saveBackupDateFormat, ".", "-");
{
// Remove all extra backups
std::string fn = fi.fileName();
Base::FileInfo di(fi.dirPath());
std::vector<Base::FileInfo> backup;
std::vector<Base::FileInfo> files = di.getDirectoryContent();
for (std::vector<Base::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) {
if (it->isFile()) {
std::string file = it->fileName();
std::string fext = it->extension();
std::string fextUp = fext;
std::transform(fextUp.begin(), fextUp.end(), fextUp.begin(),(int (*)(int))toupper);
// re-enforcing identification of the backup file
// old case : the name starts with the full name of the project and follows with numbers
if ((startsWith(file, fn) &&
(file.length() > fn.length()) &&
checkDigits(file.substr(fn.length()))) ||
// .FCBak case : The bame starts with the base name of the project + "."
// + complement with no "." + ".FCBak"
((fextUp == "FCBAK") && startsWith(file, pbn) &&
(checkValidComplement(file, pbn, fext)))) {
backup.push_back(*it);
}
}
}
if (!backup.empty() && (int)backup.size() >= numberOfFiles) {
std::sort (backup.begin(), backup.end(), fileComparisonByDate);
// delete the oldest backup file we found
// Base::FileInfo del = backup.front();
int nb = 0;
for (std::vector<Base::FileInfo>::iterator it = backup.begin(); it != backup.end(); ++it) {
nb++;
if (nb >= numberOfFiles) {
try {
if (!it->deleteFile()) {
backupManagementError = true;
Base::Console().Warning("Cannot remove backup file : %s\n", it->fileName().c_str());
}
}
catch (...) {
backupManagementError = true;
Base::Console().Warning("Cannot remove backup file : %s\n", it->fileName().c_str());
}
}
}
}
} //end remove backup
// create a new backup file
{
int ext = 1;
if (useFCBakExtension) {
std::stringstream str;
Base::TimeInfo ti = fi.lastModified();
time_t s =ti.getSeconds();
struct tm * timeinfo = localtime(& s);
char buffer[100];
strftime(buffer,sizeof(buffer),saveBackupDateFormat.c_str(),timeinfo);
str << bn << buffer ;
fn = str.str();
bool done = false;
if ((fn == "") || (fn[fn.length()-1] == ' ') || (fn[fn.length()-1] == '-')) {
if (fn[fn.length()-1] == ' ') {
fn = fn.substr(0,fn.length()-1);
}
}
else {
if (renameFileNoErase(fi, fn+".FCBak") == false) {
fn = fn + "-";
}
else {
done = true;
}
}
if (!done) {
while (ext < numberOfFiles + 10) {
if (renameFileNoErase(fi, fn+std::to_string(ext)+".FCBak"))
break;
ext++;
}
}
}
else {
// changed but simpler and solves also the delay sometimes introduced by google drive
while (ext < numberOfFiles + 10) {
// linux just replace the file if exists, and then the existence is to be tested before rename
if (renameFileNoErase(fi, fi.filePath()+std::to_string(ext)))
break;
ext++;
}
}
if (ext >= numberOfFiles + 10) {
Base::Console().Error("File not saved: Cannot rename project file to backup file\n");
//throw Base::FileException("File not saved: Cannot rename project file to backup file", fi);
}
}
}
Base::FileInfo tmp(sourcename);
if (tmp.renameFile(targetname.c_str()) == false) {
Base::Console().Error("Save interrupted: Cannot rename file from '%s' to '%s'\n",
sourcename.c_str(), targetname.c_str());
//throw Base::FileException("Save interrupted: Cannot rename temporary file to project file", tmp);
}
if (numberOfFiles <= 0) {
try {
fi.deleteFile();
}
catch (...) {
Base::Console().Warning("Cannot remove backup file: %s\n", fi.fileName().c_str());
backupManagementError = true;
}
}
if (backupManagementError) {
throw Base::FileException("Warning: Save complete, but error while managing backup history.", fi);
}
}
static bool fileComparisonByDate(const Base::FileInfo& i,
const Base::FileInfo& j) {
return (i.lastModified()>j.lastModified());
}
bool startsWith(const std::string& st1,
const std::string& st2) const {
return st1.substr(0,st2.length()) == st2;
}
bool checkValidString (const std::string& cmpl, const boost::regex& e) const {
boost::smatch what;
bool res = boost::regex_search (cmpl,what,e);
return res;
}
bool checkValidComplement(const std::string& file, const std::string& pbn, const std::string& ext) const {
std::string cmpl = file.substr(pbn.length(),file.length()- pbn.length() - ext.length()-1);
boost::regex e (R"(^[^.]*$)");
return checkValidString(cmpl,e);
}
bool checkDigits (const std::string& cmpl) const {
boost::regex e (R"(^[0-9]*$)");
return checkValidString(cmpl,e);
}
bool renameFileNoErase(Base::FileInfo fi, const std::string& newName) {
// linux just replaces the file if it exists, so the existence is to be tested before rename
Base::FileInfo nf(newName);
if (!nf.exists()) {
return fi.renameFile(newName.c_str());
}
return false;
}
private:
Policy policy;
int numberOfFiles;
bool useFCBakExtension;
std::string saveBackupDateFormat;
};
}
@@ -2419,9 +2602,20 @@ bool Document::saveToFile(const char* filename) const
if (!backup) {
count_bak = -1;
}
bool useFCBakExtension = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Document")->GetBool("UseFCBakExtension",false);
std::string saveBackupDateFormat = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Document")->GetASCII("SaveBackupDateFormat","%Y%m%d-%H%M%S");
BackupPolicy policy;
policy.setPolicy(BackupPolicy::Standard);
if (useFCBakExtension) {
policy.setPolicy(BackupPolicy::TimeStamp);
policy.useBackupExtension(useFCBakExtension);
policy.setDateFormat(saveBackupDateFormat);
}
else {
policy.setPolicy(BackupPolicy::Standard);
}
policy.setNumberOfFiles(count_bak);
policy.apply(fn, filename);
}