File indexing completed on 2024-12-08 05:07:32

0001 // clang-format off
0002 /*
0003  * KDiff3 - Text Diff And Merge Tool
0004  *
0005  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
0006  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 // clang-format on
0010 
0011 #ifndef FILEACCESS_H
0012 #define FILEACCESS_H
0013 
0014 #include "DirectoryList.h"
0015 
0016 #include <type_traits>
0017 
0018 #include <QDateTime>
0019 #include <QDir>
0020 #include <QFile>
0021 #include <QFileInfo>
0022 #include <QSharedPointer>
0023 #include <QTemporaryFile>
0024 #include <QUrl>
0025 
0026 #if HAS_KFKIO && !defined AUTOTEST
0027 #include <KIO/UDSEntry>
0028 #endif
0029 
0030 class FileAccessJobHandler;
0031 class DefaultFileAccessJobHandler;
0032 class IgnoreList;
0033 /*
0034   Defining a function as virtual in FileAccess is intended to allow testing sub classes to be written
0035   more easily. This way the test can use a moc class that emulates the needed conditions with no
0036   actual file being present. This would otherwise be a technical and logistical nightmare.
0037 */
0038 class FileAccess
0039 {
0040   public:
0041     FileAccess();
0042 
0043     FileAccess(const FileAccess&);
0044     FileAccess(FileAccess&&) noexcept;
0045     FileAccess& operator=(const FileAccess&);
0046     FileAccess& operator=(FileAccess&&) noexcept;
0047     virtual ~FileAccess();
0048     explicit FileAccess(const QString& name, bool bWantToWrite = false); // name: local file or dirname or url (when supported)
0049 
0050     explicit FileAccess(const QUrl& name, bool bWantToWrite = false); // name: local file or dirname or url (when supported)
0051     void setFile(const QString& name, bool bWantToWrite = false);
0052     void setFile(const QUrl& url, bool bWantToWrite = false);
0053     void setFile(FileAccess* pParent, const QFileInfo& fi);
0054 
0055     virtual void loadData();
0056 
0057     [[nodiscard]] bool isNormal() const;
0058     [[nodiscard]] bool isValid() const;
0059     [[nodiscard]] bool isBrokenLink() const { return m_bBrokenLink; }
0060     [[nodiscard]] virtual bool isFile() const;
0061     [[nodiscard]] virtual bool isDir() const;
0062     [[nodiscard]] virtual bool isSymLink() const;
0063     [[nodiscard]] virtual bool exists() const;
0064     [[nodiscard]] virtual qint64 size() const;     // Size as returned by stat().
0065     [[nodiscard]] virtual qint64 sizeForReading(); // If the size can't be determined by stat() then the file is copied to a local temp file.
0066     [[nodiscard]] virtual bool isReadable() const;
0067     [[nodiscard]] virtual bool isWritable() const;
0068     [[nodiscard]] virtual bool isExecutable() const;
0069     [[nodiscard]] virtual bool isHidden() const;
0070     [[nodiscard]] const QString& readLink() const;
0071 
0072     [[nodiscard]] const QDateTime& lastModified() const;
0073 
0074     [[nodiscard]] const QString& displayName() const { return mDisplayName.isEmpty() ? fileName() : mDisplayName; }
0075     [[nodiscard]] const QString& fileName(bool needTmp = false) const; // Just the name-part of the path, without parent directories
0076     [[nodiscard]] QString fileRelPath() const;                  // The path relative to base comparison directory
0077     [[nodiscard]] QString prettyAbsPath() const;
0078     [[nodiscard]] const QUrl& url() const;
0079     void setUrl(const QUrl& inUrl) { m_url = inUrl; }
0080 
0081     //Workaround for QUrl::toDisplayString/QUrl::toString behavior that does not fit KDiff3's expectations
0082     [[nodiscard]] QString absoluteFilePath() const;
0083     [[nodiscard]] static QString prettyAbsPath(const QUrl& url)
0084     {
0085         if(!isLocal(url)) return url.toDisplayString();
0086 
0087         //work around for bad path in windows drop event urls. (Qt 5.15.2 affected)
0088         const QString path = url.toLocalFile();
0089         if(!path.isEmpty() && !path.startsWith('/'))
0090           return path;
0091 
0092         return QFileInfo(url.path()).absoluteFilePath();
0093     }
0094 
0095     //Workaround for QUrl::isLocalFile behavior that does not fit KDiff3's expectations.
0096     [[nodiscard]] bool isLocal() const;
0097     [[nodiscard]] static bool isLocal(const QUrl& url)
0098     {
0099         return url.isLocalFile() || !url.isValid() || url.scheme().isEmpty();
0100     }
0101 
0102     virtual bool readFile(void* pDestBuffer, qint64 maxLength);
0103     virtual bool writeFile(const void* pSrcBuffer, qint64 length);
0104     bool listDir(DirectoryList* pDirList, bool bRecursive, bool bFindHidden,
0105                  const QString& filePattern, const QString& fileAntiPattern,
0106                  const QString& dirAntiPattern, bool bFollowDirLinks, IgnoreList& ignoreList) const;
0107     virtual bool copyFile(const QString& destUrl);
0108     virtual bool createBackup(const QString& bakExtension);
0109 
0110     [[nodiscard]] QString getTempName() const;
0111     virtual bool createLocalCopy();
0112     static void createTempFile(QTemporaryFile&);
0113     bool removeFile();
0114     static bool makeDir(const QString&);
0115     static bool removeDir(const QString&);
0116     static bool exists(const QString&);
0117     static QString cleanPath(const QString&);
0118 
0119     //bool chmod( const QString& );
0120     bool rename(const FileAccess&);
0121     static bool symLink(const QString& linkTarget, const QString& linkLocation);
0122 
0123     virtual void addPath(const QString& txt, bool reinit = true);
0124     [[nodiscard]] const QString& getStatusText() const;
0125 
0126     [[nodiscard]] FileAccess* parent() const; // !=0 for listDir-results, but only valid if the parent was not yet destroyed.
0127 
0128     void doError();
0129     void filterList(const QString& dir, DirectoryList* pDirList, const QString& filePattern,
0130                     const QString& fileAntiPattern, const QString& dirAntiPattern,
0131                     const IgnoreList& ignoreList);
0132 
0133     [[nodiscard]] QDir getBaseDirectory() const { return m_baseDir; }
0134 
0135     bool open(const QFile::OpenMode flags);
0136 
0137     qint64 read(char* data, const qint64 maxlen);
0138     void close();
0139 
0140     [[nodiscard]] const QString& errorString() const;
0141 
0142     //These should be exposed for auto tests
0143   protected:
0144 #if HAS_KFKIO && !defined AUTOTEST
0145     friend DefaultFileAccessJobHandler;
0146     void setFromUdsEntry(const KIO::UDSEntry& e, FileAccess* parent);
0147 #endif
0148     void setStatusText(const QString& s);
0149 
0150     void reset();
0151 
0152     bool interruptableReadFile(void* pDestBuffer, qint64 maxLength);
0153 
0154     std::unique_ptr<FileAccessJobHandler> mJobHandler;
0155     FileAccess* m_pParent = nullptr;
0156     QUrl m_url;
0157     bool m_bValidData = false;
0158 
0159     QDir m_baseDir;
0160     QFileInfo m_fileInfo;
0161     QString m_linkTarget;
0162     QString m_name;
0163 
0164     QString mDisplayName;
0165     QString m_localCopy;
0166     QString mPhysicalPath;
0167     QSharedPointer<QTemporaryFile> tmpFile = QSharedPointer<QTemporaryFile>::create();
0168     QSharedPointer<QFile> realFile = nullptr;
0169 
0170     qint64 m_size = 0;
0171     QDateTime m_modificationTime = QDateTime::fromMSecsSinceEpoch(0);
0172     bool m_bBrokenLink = false;
0173     bool m_bSymLink = false;
0174     bool m_bFile = false;
0175     bool m_bDir = false;
0176     bool m_bExists = false;
0177     bool m_bWritable = false;
0178     bool m_bReadable = false;
0179     bool m_bExecutable = false;
0180     bool m_bHidden = false;
0181 
0182     QString m_statusText; // Might contain an error string, when the last operation didn't succeed.
0183 
0184   private:
0185     /*
0186     These two variables are used to prevent infinate/long running loops when a symlinks true target
0187     must be found. isNormal is right now the only place this is needed.
0188 
0189     Never expose these outside FileAccess as they are internal values.
0190     */
0191     mutable bool mVisited = false;
0192 };
0193 /*
0194  FileAccess objects should be copy and move assignable.
0195   Used a few places in KDiff3 itself.
0196   Also used in std::list<FileAccess>
0197 */
0198 static_assert(std::is_copy_assignable<FileAccess>::value, "FileAccess must be copy assignable.");
0199 static_assert(std::is_move_assignable<FileAccess>::value, "FileAccess must be move assignable.");
0200 
0201 #endif