• Skip to content
  • Skip to link menu
KDE 4.1 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 
00031 #include <kconfig.h>
00032 #include <kde_file.h>
00033 #include <klocale.h>
00034 #include <kstandarddirs.h>
00035 #include <kconfiggroup.h>
00036 #include <kcomponentdata.h>
00037 #include "ktemporaryfile.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     KTemporaryFile tempFile(d->componentData);
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     return;
00154 }
00155 
00156 QFile::FileError KSaveFile::error() const
00157 {
00158     if ( d->error != QFile::NoError ) {
00159         return d->error;
00160     } else {
00161         return QFile::error();
00162     }
00163 }
00164 
00165 QString KSaveFile::errorString() const
00166 {
00167     if ( !d->errorString.isEmpty() ) {
00168         return d->errorString;
00169     } else {
00170         return QFile::errorString();
00171     }
00172 }
00173 
00174 QString KSaveFile::fileName() const
00175 {
00176     return d->realFileName;
00177 }
00178 
00179 void KSaveFile::abort()
00180 {
00181     if ( d->stream ) {
00182         fclose(d->stream);
00183         d->stream = 0;
00184     }
00185     
00186     close();
00187     QFile::remove(d->tempFileName); //non-static QFile::remove() does not work. 
00188     d->wasFinalized = true;
00189 }
00190 
00191 bool KSaveFile::finalize()
00192 {
00193     bool success = false;
00194 
00195     if ( !d->wasFinalized ) {
00196         if ( d->stream ) {
00197             fclose(d->stream);
00198             d->stream = 0;
00199         }
00200         close();
00201         
00202         if( error() != NoError ) {
00203             QFile::remove(d->tempFileName);
00204         }
00205         //Qt does not allow us to atomically overwrite an existing file,
00206         //so if the target file already exists, there is no way to change it
00207         //to the temp file without creating a small race condition. So we use
00208         //the standard rename call instead, which will do the copy without the
00209         //race condition.
00210         else if (0 == KDE_rename(QFile::encodeName(d->tempFileName),
00211                       QFile::encodeName(d->realFileName))) {
00212             d->error=QFile::NoError;
00213             d->errorString.clear();
00214             success = true;
00215         } else {
00216             d->error=QFile::OpenError;
00217             d->errorString=i18n("Error during rename.");
00218             QFile::remove(d->tempFileName);
00219         }
00220 
00221         d->wasFinalized = true;
00222     }
00223 
00224     return success;
00225 }
00226 
00227 bool KSaveFile::backupFile( const QString& qFilename, const QString& backupDir )
00228 {
00229     // get backup type from config, by default use "simple"
00230     // get extension from config, by default use "~"
00231     // get max number of backups from config, by default set to 10
00232 
00233     KConfigGroup g(KGlobal::config(), "Backups"); // look in the Backups section
00234     QString type = g.readEntry( "Type", "simple" );
00235     QString extension = g.readEntry( "Extension", "~" );
00236     QString message = g.readEntry( "Message", "Automated KDE Commit" );
00237     int maxnum = g.readEntry( "MaxBackups", 10 );
00238     if ( type.toLower() == "numbered" ) {
00239         return( numberedBackupFile( qFilename, backupDir, extension, maxnum ) );
00240     } else if ( type.toLower() == "rcs" ) {
00241         return( rcsBackupFile( qFilename, backupDir, message ) );
00242     } else {
00243         return( simpleBackupFile( qFilename, backupDir, extension ) );
00244     }
00245 }
00246 
00247 bool KSaveFile::simpleBackupFile( const QString& qFilename,
00248                                   const QString& backupDir,
00249                                   const QString& backupExtension )
00250 {
00251     QString backupFileName = qFilename + backupExtension;
00252     
00253     if ( !backupDir.isEmpty() ) {
00254         QFileInfo fileInfo ( qFilename );
00255         backupFileName = backupDir + '/' + fileInfo.fileName() + backupExtension;
00256     }
00257 
00258 //    kDebug(180) << "KSaveFile copying " << qFilename << " to " << backupFileName;
00259     return QFile::copy(qFilename, backupFileName);
00260 }
00261 
00262 bool KSaveFile::rcsBackupFile( const QString& qFilename,
00263                                const QString& backupDir,
00264                                const QString& backupMessage )
00265 {
00266     QFileInfo fileInfo ( qFilename );
00267     
00268     QString qBackupFilename;
00269     if ( backupDir.isEmpty() ) {
00270         qBackupFilename = qFilename;
00271     } else {
00272         qBackupFilename = backupDir + fileInfo.fileName();
00273     }
00274     qBackupFilename += QString::fromLatin1( ",v" );
00275 
00276     // If backupDir is specified, copy qFilename to the
00277     // backupDir and perform the commit there, unlinking
00278     // backupDir/qFilename when finished.
00279     if ( !backupDir.isEmpty() )
00280     {
00281         if ( !QFile::copy(qFilename, backupDir + fileInfo.fileName()) ) {
00282             return false;
00283         }
00284         fileInfo.setFile(backupDir + '/' + fileInfo.fileName());
00285     }
00286     
00287     QString cipath = KStandardDirs::findExe("ci");
00288     QString copath = KStandardDirs::findExe("co");
00289     QString rcspath = KStandardDirs::findExe("rcs");
00290     if ( cipath.isEmpty() || copath.isEmpty() || rcspath.isEmpty() )
00291         return false;
00292     
00293     // Check in the file unlocked with 'ci'
00294     QProcess ci;
00295     if ( !backupDir.isEmpty() )
00296         ci.setWorkingDirectory( backupDir );
00297     ci.start( cipath, QStringList() << "-u" << fileInfo.filePath() );
00298     if ( !ci.waitForStarted() )
00299         return false;
00300     ci.write( backupMessage.toLatin1() );
00301     ci.write(".");
00302     ci.closeWriteChannel();
00303     if( !ci.waitForFinished() )
00304         return false;
00305 
00306     // Use 'rcs' to unset strict locking
00307     QProcess rcs;
00308     if ( !backupDir.isEmpty() )
00309         rcs.setWorkingDirectory( backupDir );
00310     rcs.start( rcspath, QStringList() << "-U" << qBackupFilename );
00311     if ( !rcs.waitForFinished() )
00312         return false;
00313 
00314     // Use 'co' to checkout the current revision and restore permissions
00315     QProcess co;
00316     if ( !backupDir.isEmpty() )
00317         co.setWorkingDirectory( backupDir );
00318     co.start( copath, QStringList() << qBackupFilename );
00319     if ( !co.waitForFinished() )
00320         return false;
00321 
00322     if ( !backupDir.isEmpty() ) {
00323         return QFile::remove( fileInfo.filePath() );
00324     } else {
00325         return true;
00326     }
00327 }
00328 
00329 bool KSaveFile::numberedBackupFile( const QString& qFilename,
00330                                     const QString& backupDir,
00331                                     const QString& backupExtension,
00332                                     const uint maxBackups )
00333 {
00334     QFileInfo fileInfo ( qFilename );
00335     
00336     // The backup file name template.
00337     QString sTemplate;
00338     if ( backupDir.isEmpty() ) {
00339         sTemplate = qFilename + ".%1" + backupExtension;
00340     } else {
00341         sTemplate = backupDir + '/' + fileInfo.fileName() + ".%1" + backupExtension;
00342     }
00343 
00344     // First, search backupDir for numbered backup files to remove.
00345     // Remove all with number 'maxBackups' and greater.
00346     QDir d = backupDir.isEmpty() ? fileInfo.dir() : backupDir;
00347     d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
00348     QStringList nameFilters = QStringList( fileInfo.fileName() + ".*" + backupExtension );
00349     d.setNameFilters( nameFilters );
00350     d.setSorting( QDir::Name );
00351 
00352     uint maxBackupFound = 0;
00353     foreach ( const QFileInfo &fi, d.entryInfoList() ) {
00354         if ( fi.fileName().endsWith( backupExtension ) ) {
00355             // sTemp holds the file name, without the ending backupExtension
00356             QString sTemp = fi.fileName();
00357             sTemp.truncate( fi.fileName().length()-backupExtension.length() );
00358             // compute the backup number
00359             int idex = sTemp.lastIndexOf( '.' );
00360             if ( idex > 0 ) {
00361                 bool ok;
00362                 uint num = sTemp.mid( idex+1 ).toUInt( &ok );
00363                 if ( ok ) {
00364                     if ( num >= maxBackups ) {
00365                         QFile::remove( fi.filePath() );
00366                     } else {
00367                         maxBackupFound = qMax( maxBackupFound, num );
00368                     }
00369                 }
00370             }
00371         }
00372     }
00373 
00374     // Next, rename max-1 to max, max-2 to max-1, etc.
00375     QString to=sTemplate.arg( maxBackupFound+1 );
00376     for ( int i=maxBackupFound; i>0; i-- ) {
00377         QString from = sTemplate.arg( i );
00378 //        kDebug(180) << "KSaveFile renaming " << from << " to " << to;
00379         QFile::rename( from, to );
00380         to = from;
00381     }
00382 
00383     // Finally create most recent backup by copying the file to backup number 1.
00384 //    kDebug(180) << "KSaveFile copying " << qFilename << " to " << sTemplate.arg(1);
00385     return QFile::copy(qFilename, sTemplate.arg(1));
00386 }
00387 

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
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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