• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

ksavefile.cpp

Go to the documentation of this file.
00001 /* kate: tab-indents off; replace-tabs on; tab-width 4; remove-trailing-space on; encoding utf-8;*/
00002 /*
00003   This file is part of the KDE libraries
00004   Copyright 1999 Waldo Bastian <bastian@kde.org>
00005   Copyright 2006 Allen Winter <winter@kde.org>
00006   Copyright 2006 Gregory S. Hayes <syncomm@kde.org>
00007   Copyright 2006 Jaison Lee <lee.jaison@gmail.com>
00008 
00009   This library is free software; you can redistribute it and/or
00010   modify it under the terms of the GNU Library General Public
00011   License version 2 as published by the Free Software Foundation.
00012 
00013   This library is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016   Library General Public License for more details.
00017 
00018   You should have received a copy of the GNU Library General Public License
00019   along with this library; see the file COPYING.LIB.  If not, write to
00020   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021   Boston, MA 02110-1301, USA.
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 class KSaveFile::Private
00040 {
00041 public:
00042     QString realFileName; //The name of the end-result file
00043     QString tempFileName; //The name of the temp file we are using
00044     
00045     QFile::FileError error;
00046     QString errorString;
00047     bool wasFinalized;
00048     FILE *stream;
00049     KComponentData componentData;
00050 
00051     Private(const KComponentData &c)
00052         : componentData(c)
00053     {
00054         error = QFile::NoError;
00055         wasFinalized = false;
00056         stream = 0;
00057     }
00058 };
00059 
00060 KSaveFile::KSaveFile()
00061  : d(new Private(KGlobal::mainComponent()))
00062 {
00063 }
00064 
00065 KSaveFile::KSaveFile(const QString &filename, const KComponentData &componentData)
00066  : d(new Private(componentData))
00067 {
00068     KSaveFile::setFileName(filename);
00069 }
00070 
00071 KSaveFile::~KSaveFile()
00072 {
00073     if (!d->wasFinalized)
00074         finalize();
00075 
00076     delete d;
00077 }
00078 
00079 bool KSaveFile::open(OpenMode flags)
00080 {
00081     if ( d->realFileName.isNull() ) {
00082         d->error=QFile::OpenError;
00083         d->errorString=i18n("No target filename has been given.");
00084         return false;
00085     }
00086 
00087     if ( !d->tempFileName.isNull() ) {
00088 #if 0 // do not set an error here, this open() fails, but the file itself is without errors
00089         d->error=QFile::OpenError;
00090         d->errorString=i18n("Already opened.");
00091 #endif
00092         return false;
00093     }
00094 
00095     // we only check here if the directory can be written to
00096     // the actual filename isn't written to, but replaced later
00097     // with the contents of our tempfile
00098     if (!KStandardDirs::checkAccess(d->realFileName, W_OK)) {
00099         d->error=QFile::PermissionsError;
00100         d->errorString=i18n("Insufficient permissions in target directory.");
00101         return false;
00102     }
00103 
00104     //Create our temporary file
00105     QTemporaryFile tempFile;
00106     tempFile.setAutoRemove(false);
00107     tempFile.setFileTemplate(d->realFileName + "XXXXXX.new");
00108     if (!tempFile.open()) {
00109         d->error=QFile::OpenError;
00110         d->errorString=i18n("Unable to open temporary file.");
00111         return false;
00112     }
00113     
00114     // if we're overwriting an existing file, ensure temp file's
00115     // permissions are the same as existing file so the existing
00116     // file's permissions are preserved. this will succeed only if we
00117     // are the same owner and group - or allmighty root.
00118     QFileInfo fi ( d->realFileName );
00119     if (fi.exists()) {
00120         //Qt apparently has no way to change owner/group of file :(
00121         if (!fchown(tempFile.handle(), fi.ownerId(), fi.groupId()))
00122             tempFile.setPermissions(fi.permissions());
00123     }
00124     else {
00125         mode_t umsk = KGlobal::umask();
00126         fchmod(tempFile.handle(), 0666&(~umsk));
00127     }
00128 
00129     //Open oursleves with the temporary file
00130     QFile::setFileName(tempFile.fileName());
00131     if (!QFile::open(flags)) {
00132         tempFile.setAutoRemove(true);
00133         return false;
00134     }
00135 
00136     d->tempFileName = tempFile.fileName();
00137     d->error=QFile::NoError;
00138     d->errorString.clear();
00139     return true;
00140 }
00141 
00142 void KSaveFile::setFileName(const QString &filename)
00143 {
00144     d->realFileName = filename;
00145     
00146     // make absolute if needed
00147     if ( QDir::isRelativePath( filename ) ) {
00148         d->realFileName = QDir::current().absoluteFilePath( filename );
00149     }
00150 
00151     // follow symbolic link, if any
00152     d->realFileName = KStandardDirs::realFilePath( d->realFileName );
00153 
00154 #ifdef Q_WS_WIN
00155     d->realFileName.replace(QString::fromLatin1("//"), QString::fromLatin1("/"));
00156 #endif
00157     return;
00158 }
00159 
00160 QFile::FileError KSaveFile::error() const
00161 {
00162     if ( d->error != QFile::NoError ) {
00163         return d->error;
00164     } else {
00165         return QFile::error();
00166     }
00167 }
00168 
00169 QString KSaveFile::errorString() const
00170 {
00171     if ( !d->errorString.isEmpty() ) {
00172         return d->errorString;
00173     } else {
00174         return QFile::errorString();
00175     }
00176 }
00177 
00178 QString KSaveFile::fileName() const
00179 {
00180     return d->realFileName;
00181 }
00182 
00183 void KSaveFile::abort()
00184 {
00185     if ( d->stream ) {
00186         fclose(d->stream);
00187         d->stream = 0;
00188     }
00189     
00190     close();
00191     QFile::remove(d->tempFileName); //non-static QFile::remove() does not work. 
00192     d->wasFinalized = true;
00193 }
00194 
00195 bool KSaveFile::finalize()
00196 {
00197     bool success = false;
00198 
00199     if ( !d->wasFinalized ) {
00200         if ( d->stream ) {
00201             fclose(d->stream);
00202             d->stream = 0;
00203         }
00204         close();
00205         
00206         if( error() != NoError ) {
00207             QFile::remove(d->tempFileName);
00208         }
00209         //Qt does not allow us to atomically overwrite an existing file,
00210         //so if the target file already exists, there is no way to change it
00211         //to the temp file without creating a small race condition. So we use
00212         //the standard rename call instead, which will do the copy without the
00213         //race condition.
00214         else if (0 == KDE_rename(QFile::encodeName(d->tempFileName),
00215                       QFile::encodeName(d->realFileName))) {
00216             d->error=QFile::NoError;
00217             d->errorString.clear();
00218             success = true;
00219         } else {
00220             d->error=QFile::OpenError;
00221             d->errorString=i18n("Error during rename.");
00222             QFile::remove(d->tempFileName);
00223         }
00224 
00225         d->wasFinalized = true;
00226     }
00227 
00228     return success;
00229 }
00230 
00231 bool KSaveFile::backupFile( const QString& qFilename, const QString& backupDir )
00232 {
00233     // get backup type from config, by default use "simple"
00234     // get extension from config, by default use "~"
00235     // get max number of backups from config, by default set to 10
00236 
00237     KConfigGroup g(KGlobal::config(), "Backups"); // look in the Backups section
00238     QString type = g.readEntry( "Type", "simple" );
00239     QString extension = g.readEntry( "Extension", "~" );
00240     QString message = g.readEntry( "Message", "Automated KDE Commit" );
00241     int maxnum = g.readEntry( "MaxBackups", 10 );
00242     if ( type.toLower() == "numbered" ) {
00243         return( numberedBackupFile( qFilename, backupDir, extension, maxnum ) );
00244     } else if ( type.toLower() == "rcs" ) {
00245         return( rcsBackupFile( qFilename, backupDir, message ) );
00246     } else {
00247         return( simpleBackupFile( qFilename, backupDir, extension ) );
00248     }
00249 }
00250 
00251 bool KSaveFile::simpleBackupFile( const QString& qFilename,
00252                                   const QString& backupDir,
00253                                   const QString& backupExtension )
00254 {
00255     QString backupFileName = qFilename + backupExtension;
00256     
00257     if ( !backupDir.isEmpty() ) {
00258         QFileInfo fileInfo ( qFilename );
00259         backupFileName = backupDir + '/' + fileInfo.fileName() + backupExtension;
00260     }
00261 
00262 //    kDebug(180) << "KSaveFile copying " << qFilename << " to " << backupFileName;
00263     QFile::remove(backupFileName);
00264     return QFile::copy(qFilename, backupFileName);
00265 }
00266 
00267 bool KSaveFile::rcsBackupFile( const QString& qFilename,
00268                                const QString& backupDir,
00269                                const QString& backupMessage )
00270 {
00271     QFileInfo fileInfo ( qFilename );
00272     
00273     QString qBackupFilename;
00274     if ( backupDir.isEmpty() ) {
00275         qBackupFilename = qFilename;
00276     } else {
00277         qBackupFilename = backupDir + fileInfo.fileName();
00278     }
00279     qBackupFilename += QString::fromLatin1( ",v" );
00280 
00281     // If backupDir is specified, copy qFilename to the
00282     // backupDir and perform the commit there, unlinking
00283     // backupDir/qFilename when finished.
00284     if ( !backupDir.isEmpty() )
00285     {
00286         if ( !QFile::copy(qFilename, backupDir + fileInfo.fileName()) ) {
00287             return false;
00288         }
00289         fileInfo.setFile(backupDir + '/' + fileInfo.fileName());
00290     }
00291     
00292     QString cipath = KStandardDirs::findExe("ci");
00293     QString copath = KStandardDirs::findExe("co");
00294     QString rcspath = KStandardDirs::findExe("rcs");
00295     if ( cipath.isEmpty() || copath.isEmpty() || rcspath.isEmpty() )
00296         return false;
00297     
00298     // Check in the file unlocked with 'ci'
00299     QProcess ci;
00300     if ( !backupDir.isEmpty() )
00301         ci.setWorkingDirectory( backupDir );
00302     ci.start( cipath, QStringList() << "-u" << fileInfo.filePath() );
00303     if ( !ci.waitForStarted() )
00304         return false;
00305     ci.write( backupMessage.toLatin1() );
00306     ci.write(".");
00307     ci.closeWriteChannel();
00308     if( !ci.waitForFinished() )
00309         return false;
00310 
00311     // Use 'rcs' to unset strict locking
00312     QProcess rcs;
00313     if ( !backupDir.isEmpty() )
00314         rcs.setWorkingDirectory( backupDir );
00315     rcs.start( rcspath, QStringList() << "-U" << qBackupFilename );
00316     if ( !rcs.waitForFinished() )
00317         return false;
00318 
00319     // Use 'co' to checkout the current revision and restore permissions
00320     QProcess co;
00321     if ( !backupDir.isEmpty() )
00322         co.setWorkingDirectory( backupDir );
00323     co.start( copath, QStringList() << qBackupFilename );
00324     if ( !co.waitForFinished() )
00325         return false;
00326 
00327     if ( !backupDir.isEmpty() ) {
00328         return QFile::remove( fileInfo.filePath() );
00329     } else {
00330         return true;
00331     }
00332 }
00333 
00334 bool KSaveFile::numberedBackupFile( const QString& qFilename,
00335                                     const QString& backupDir,
00336                                     const QString& backupExtension,
00337                                     const uint maxBackups )
00338 {
00339     QFileInfo fileInfo ( qFilename );
00340     
00341     // The backup file name template.
00342     QString sTemplate;
00343     if ( backupDir.isEmpty() ) {
00344         sTemplate = qFilename + ".%1" + backupExtension;
00345     } else {
00346         sTemplate = backupDir + '/' + fileInfo.fileName() + ".%1" + backupExtension;
00347     }
00348 
00349     // First, search backupDir for numbered backup files to remove.
00350     // Remove all with number 'maxBackups' and greater.
00351     QDir d = backupDir.isEmpty() ? fileInfo.dir() : backupDir;
00352     d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
00353     QStringList nameFilters = QStringList( fileInfo.fileName() + ".*" + backupExtension );
00354     d.setNameFilters( nameFilters );
00355     d.setSorting( QDir::Name );
00356 
00357     uint maxBackupFound = 0;
00358     foreach ( const QFileInfo &fi, d.entryInfoList() ) {
00359         if ( fi.fileName().endsWith( backupExtension ) ) {
00360             // sTemp holds the file name, without the ending backupExtension
00361             QString sTemp = fi.fileName();
00362             sTemp.truncate( fi.fileName().length()-backupExtension.length() );
00363             // compute the backup number
00364             int idex = sTemp.lastIndexOf( '.' );
00365             if ( idex > 0 ) {
00366                 bool ok;
00367                 uint num = sTemp.mid( idex+1 ).toUInt( &ok );
00368                 if ( ok ) {
00369                     if ( num >= maxBackups ) {
00370                         QFile::remove( fi.filePath() );
00371                     } else {
00372                         maxBackupFound = qMax( maxBackupFound, num );
00373                     }
00374                 }
00375             }
00376         }
00377     }
00378 
00379     // Next, rename max-1 to max, max-2 to max-1, etc.
00380     QString to=sTemplate.arg( maxBackupFound+1 );
00381     for ( int i=maxBackupFound; i>0; i-- ) {
00382         QString from = sTemplate.arg( i );
00383 //        kDebug(180) << "KSaveFile renaming " << from << " to " << to;
00384         QFile::rename( from, to );
00385         to = from;
00386     }
00387 
00388     // Finally create most recent backup by copying the file to backup number 1.
00389 //    kDebug(180) << "KSaveFile copying " << qFilename << " to " << sTemplate.arg(1);
00390     return QFile::copy(qFilename, sTemplate.arg(1));
00391 }
00392 

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal