KDECore
ksavefile.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "ksavefile.h"
00025
00026 #include <config.h>
00027
00028 #include <QtCore/QDir>
00029 #include <QProcess>
00030 #include <QTemporaryFile>
00031
00032 #include <kconfig.h>
00033 #include <kde_file.h>
00034 #include <klocale.h>
00035 #include <kstandarddirs.h>
00036 #include <kconfiggroup.h>
00037 #include <kcomponentdata.h>
00038
00039 #include <stdlib.h>
00040 #include <errno.h>
00041
00042 class KSaveFile::Private
00043 {
00044 public:
00045 QString realFileName;
00046 QString tempFileName;
00047
00048 QFile::FileError error;
00049 QString errorString;
00050 bool wasFinalized;
00051 KComponentData componentData;
00052
00053 Private(const KComponentData &c)
00054 : componentData(c)
00055 {
00056 error = QFile::NoError;
00057 wasFinalized = false;
00058 }
00059 };
00060
00061 KSaveFile::KSaveFile()
00062 : d(new Private(KGlobal::mainComponent()))
00063 {
00064 }
00065
00066 KSaveFile::KSaveFile(const QString &filename, const KComponentData &componentData)
00067 : d(new Private(componentData))
00068 {
00069 KSaveFile::setFileName(filename);
00070 }
00071
00072 KSaveFile::~KSaveFile()
00073 {
00074 if (!d->wasFinalized)
00075 finalize();
00076
00077 delete d;
00078 }
00079
00080 bool KSaveFile::open(OpenMode flags)
00081 {
00082 if ( d->realFileName.isNull() ) {
00083 d->error=QFile::OpenError;
00084 d->errorString=i18n("No target filename has been given.");
00085 return false;
00086 }
00087
00088 if ( !d->tempFileName.isNull() ) {
00089 #if 0 // do not set an error here, this open() fails, but the file itself is without errors
00090 d->error=QFile::OpenError;
00091 d->errorString=i18n("Already opened.");
00092 #endif
00093 return false;
00094 }
00095
00096
00097
00098
00099 if (!KStandardDirs::checkAccess(d->realFileName, W_OK)) {
00100 d->error=QFile::PermissionsError;
00101 d->errorString=i18n("Insufficient permissions in target directory.");
00102 return false;
00103 }
00104
00105
00106 QTemporaryFile tempFile;
00107 tempFile.setAutoRemove(false);
00108 tempFile.setFileTemplate(d->realFileName + "XXXXXX.new");
00109 if (!tempFile.open()) {
00110 d->error=QFile::OpenError;
00111 d->errorString=i18n("Unable to open temporary file.");
00112 return false;
00113 }
00114
00115
00116
00117
00118
00119 QFileInfo fi ( d->realFileName );
00120 if (fi.exists()) {
00121
00122 if (!fchown(tempFile.handle(), fi.ownerId(), fi.groupId()))
00123 tempFile.setPermissions(fi.permissions());
00124 }
00125 else {
00126 mode_t umsk = KGlobal::umask();
00127 fchmod(tempFile.handle(), 0666&(~umsk));
00128 }
00129
00130
00131 QFile::setFileName(tempFile.fileName());
00132 if (!QFile::open(flags)) {
00133 tempFile.setAutoRemove(true);
00134 return false;
00135 }
00136
00137 d->tempFileName = tempFile.fileName();
00138 d->error=QFile::NoError;
00139 d->errorString.clear();
00140 return true;
00141 }
00142
00143 void KSaveFile::setFileName(const QString &filename)
00144 {
00145 d->realFileName = filename;
00146
00147
00148 if ( QDir::isRelativePath( filename ) ) {
00149 d->realFileName = QDir::current().absoluteFilePath( filename );
00150 }
00151
00152
00153 d->realFileName = KStandardDirs::realFilePath( d->realFileName );
00154
00155 return;
00156 }
00157
00158 QFile::FileError KSaveFile::error() const
00159 {
00160 if ( d->error != QFile::NoError ) {
00161 return d->error;
00162 } else {
00163 return QFile::error();
00164 }
00165 }
00166
00167 QString KSaveFile::errorString() const
00168 {
00169 if ( !d->errorString.isEmpty() ) {
00170 return d->errorString;
00171 } else {
00172 return QFile::errorString();
00173 }
00174 }
00175
00176 QString KSaveFile::fileName() const
00177 {
00178 return d->realFileName;
00179 }
00180
00181 void KSaveFile::abort()
00182 {
00183 close();
00184 QFile::remove(d->tempFileName);
00185 d->wasFinalized = true;
00186 }
00187
00188 #ifdef HAVE_FDATASYNC
00189 # define FDATASYNC fdatasync
00190 #else
00191 # define FDATASYNC fsync
00192 #endif
00193
00194 bool KSaveFile::finalize()
00195 {
00196 bool success = false;
00197
00198 if ( !d->wasFinalized ) {
00199
00200 #ifdef Q_OS_UNIX
00201 static int extraSync = -1;
00202 if (extraSync < 0)
00203 extraSync = getenv("KDE_EXTRA_FSYNC") != 0 ? 1 : 0;
00204 if (extraSync) {
00205 if (flush()) {
00206 forever {
00207 if (!FDATASYNC(handle()))
00208 break;
00209 if (errno != EINTR) {
00210 d->error = QFile::WriteError;
00211 d->errorString = i18n("Synchronization to disk failed");
00212 break;
00213 }
00214 }
00215 }
00216 }
00217 #endif
00218
00219 close();
00220
00221 if( error() != NoError ) {
00222 QFile::remove(d->tempFileName);
00223 }
00224
00225
00226
00227
00228
00229 else if (0 == KDE::rename(d->tempFileName,d->realFileName)) {
00230 d->error=QFile::NoError;
00231 d->errorString.clear();
00232 success = true;
00233 } else {
00234 d->error=QFile::OpenError;
00235 d->errorString=i18n("Error during rename.");
00236 QFile::remove(d->tempFileName);
00237 }
00238
00239 d->wasFinalized = true;
00240 }
00241
00242 return success;
00243 }
00244
00245 #undef FDATASYNC
00246
00247 bool KSaveFile::backupFile( const QString& qFilename, const QString& backupDir )
00248 {
00249
00250
00251
00252
00253 KConfigGroup g(KGlobal::config(), "Backups");
00254 QString type = g.readEntry( "Type", "simple" );
00255 QString extension = g.readEntry( "Extension", "~" );
00256 QString message = g.readEntry( "Message", "Automated KDE Commit" );
00257 int maxnum = g.readEntry( "MaxBackups", 10 );
00258 if ( type.toLower() == "numbered" ) {
00259 return( numberedBackupFile( qFilename, backupDir, extension, maxnum ) );
00260 } else if ( type.toLower() == "rcs" ) {
00261 return( rcsBackupFile( qFilename, backupDir, message ) );
00262 } else {
00263 return( simpleBackupFile( qFilename, backupDir, extension ) );
00264 }
00265 }
00266
00267 bool KSaveFile::simpleBackupFile( const QString& qFilename,
00268 const QString& backupDir,
00269 const QString& backupExtension )
00270 {
00271 QString backupFileName = qFilename + backupExtension;
00272
00273 if ( !backupDir.isEmpty() ) {
00274 QFileInfo fileInfo ( qFilename );
00275 backupFileName = backupDir + '/' + fileInfo.fileName() + backupExtension;
00276 }
00277
00278
00279 QFile::remove(backupFileName);
00280 return QFile::copy(qFilename, backupFileName);
00281 }
00282
00283 bool KSaveFile::rcsBackupFile( const QString& qFilename,
00284 const QString& backupDir,
00285 const QString& backupMessage )
00286 {
00287 QFileInfo fileInfo ( qFilename );
00288
00289 QString qBackupFilename;
00290 if ( backupDir.isEmpty() ) {
00291 qBackupFilename = qFilename;
00292 } else {
00293 qBackupFilename = backupDir + fileInfo.fileName();
00294 }
00295 qBackupFilename += QString::fromLatin1( ",v" );
00296
00297
00298
00299
00300 if ( !backupDir.isEmpty() )
00301 {
00302 if ( !QFile::copy(qFilename, backupDir + fileInfo.fileName()) ) {
00303 return false;
00304 }
00305 fileInfo.setFile(backupDir + '/' + fileInfo.fileName());
00306 }
00307
00308 QString cipath = KStandardDirs::findExe("ci");
00309 QString copath = KStandardDirs::findExe("co");
00310 QString rcspath = KStandardDirs::findExe("rcs");
00311 if ( cipath.isEmpty() || copath.isEmpty() || rcspath.isEmpty() )
00312 return false;
00313
00314
00315 QProcess ci;
00316 if ( !backupDir.isEmpty() )
00317 ci.setWorkingDirectory( backupDir );
00318 ci.start( cipath, QStringList() << "-u" << fileInfo.filePath() );
00319 if ( !ci.waitForStarted() )
00320 return false;
00321 ci.write( backupMessage.toLatin1() );
00322 ci.write(".");
00323 ci.closeWriteChannel();
00324 if( !ci.waitForFinished() )
00325 return false;
00326
00327
00328 QProcess rcs;
00329 if ( !backupDir.isEmpty() )
00330 rcs.setWorkingDirectory( backupDir );
00331 rcs.start( rcspath, QStringList() << "-U" << qBackupFilename );
00332 if ( !rcs.waitForFinished() )
00333 return false;
00334
00335
00336 QProcess co;
00337 if ( !backupDir.isEmpty() )
00338 co.setWorkingDirectory( backupDir );
00339 co.start( copath, QStringList() << qBackupFilename );
00340 if ( !co.waitForFinished() )
00341 return false;
00342
00343 if ( !backupDir.isEmpty() ) {
00344 return QFile::remove( fileInfo.filePath() );
00345 } else {
00346 return true;
00347 }
00348 }
00349
00350 bool KSaveFile::numberedBackupFile( const QString& qFilename,
00351 const QString& backupDir,
00352 const QString& backupExtension,
00353 const uint maxBackups )
00354 {
00355 QFileInfo fileInfo ( qFilename );
00356
00357
00358 QString sTemplate;
00359 if ( backupDir.isEmpty() ) {
00360 sTemplate = qFilename + ".%1" + backupExtension;
00361 } else {
00362 sTemplate = backupDir + '/' + fileInfo.fileName() + ".%1" + backupExtension;
00363 }
00364
00365
00366
00367 QDir d = backupDir.isEmpty() ? fileInfo.dir() : backupDir;
00368 d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
00369 QStringList nameFilters = QStringList( fileInfo.fileName() + ".*" + backupExtension );
00370 d.setNameFilters( nameFilters );
00371 d.setSorting( QDir::Name );
00372
00373 uint maxBackupFound = 0;
00374 foreach ( const QFileInfo &fi, d.entryInfoList() ) {
00375 if ( fi.fileName().endsWith( backupExtension ) ) {
00376
00377 QString sTemp = fi.fileName();
00378 sTemp.truncate( fi.fileName().length()-backupExtension.length() );
00379
00380 int idex = sTemp.lastIndexOf( '.' );
00381 if ( idex > 0 ) {
00382 bool ok;
00383 uint num = sTemp.mid( idex+1 ).toUInt( &ok );
00384 if ( ok ) {
00385 if ( num >= maxBackups ) {
00386 QFile::remove( fi.filePath() );
00387 } else {
00388 maxBackupFound = qMax( maxBackupFound, num );
00389 }
00390 }
00391 }
00392 }
00393 }
00394
00395
00396 QString to=sTemplate.arg( maxBackupFound+1 );
00397 for ( int i=maxBackupFound; i>0; i-- ) {
00398 QString from = sTemplate.arg( i );
00399
00400 QFile::rename( from, to );
00401 to = from;
00402 }
00403
00404
00405
00406 return QFile::copy(qFilename, sTemplate.arg(1));
00407 }
00408