File indexing completed on 2024-12-22 04:35:00

0001 /*
0002     SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies).
0003     Contact: Qt Software Information (qt-info@nokia.com)
0004 
0005     This file is part of the QtCore module of the Qt Toolkit.
0006 
0007     $QT_BEGIN_LICENSE:LGPL$
0008     Commercial Usage
0009     Licensees holding valid Qt Commercial licenses may use this file in
0010     accordance with the Qt Commercial License Agreement provided with the
0011     Software or, alternatively, in accordance with the terms contained in
0012     a written agreement between you and Nokia.
0013 
0014     GNU Lesser General Public License Usage
0015     Alternatively, this file may be used under the terms of the GNU Lesser
0016     General Public License version 2.1 as published by the Free Software
0017     Foundation and appearing in the file LICENSE.LGPL included in the
0018     packaging of this file.  Please review the following information to
0019     ensure the GNU Lesser General Public License version 2.1 requirements
0020     will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
0021 
0022     In addition, as a special exception, Nokia gives you certain
0023     additional rights. These rights are described in the Nokia Qt LGPL
0024     Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
0025     package.
0026 
0027     GNU General Public License Usage
0028     Alternatively, this file may be used under the terms of the GNU
0029     General Public License version 3.0 as published by the Free Software
0030     Foundation and appearing in the file LICENSE.GPL included in the
0031     packaging of this file.  Please review the following information to
0032     ensure the GNU General Public License version 3.0 requirements will be
0033     met: https://www.gnu.org/licenses/gpl-3.0.html.
0034 
0035     If you are unsure which license is appropriate for your use, please
0036     contact the sales department at qt-sales@nokia.com.
0037     $QT_END_LICENSE$
0038 
0039 */
0040 
0041 #include "qfsfileengine_p.h"
0042 #include "qfsfileengine_iterator_p.h"
0043 #include "qdatetime.h"
0044 #include "qdiriterator.h"
0045 #include "qset.h"
0046 
0047 #ifndef QT_NO_FSFILEENGINE
0048 
0049 #if !defined(Q_OS_WINCE)
0050 #include <errno.h>
0051 #endif
0052 #include <stdio.h>
0053 
0054 QT_BEGIN_NAMESPACE
0055 
0056 #ifdef Q_OS_WIN
0057 #  ifndef S_ISREG
0058 #    define S_ISREG(x)   (((x) & S_IFMT) == S_IFREG)
0059 #  endif
0060 #  ifndef S_ISCHR
0061 #    define S_ISCHR(x)   (((x) & S_IFMT) == S_IFCHR)
0062 #  endif
0063 #  ifndef S_ISFIFO
0064 #    define S_ISFIFO(x) false
0065 #  endif
0066 #  ifndef S_ISSOCK
0067 #    define S_ISSOCK(x) false
0068 #  endif
0069 #  ifndef INVALID_FILE_ATTRIBUTES
0070 #    define INVALID_FILE_ATTRIBUTES (DWORD (-1))
0071 #  endif
0072 #endif
0073 
0074 
0075 /*! \class QFSFileEngine
0076     \brief The QFSFileEngine class implements Qt's default file engine.
0077     \since 4.1
0078 
0079     This class is part of the file engine framework in Qt. If you only want to
0080     access files or directories, use QFile, QFileInfo or QDir instead.
0081 
0082     QFSFileEngine is the default file engine for accessing regular files. It
0083     is provided for convenience; by subclassing this class, you can alter its
0084     behavior slightly, without having to write a complete QAbstractFileEngine
0085     subclass. To install your custom file engine, you must also subclass
0086     QAbstractFileEngineHandler and create an instance of your handler.
0087 
0088     It can also be useful to create a QFSFileEngine object directly if you
0089     need to use the local file system inside QAbstractFileEngine::create(), in
0090     order to avoid recursion (as higher-level classes tend to call
0091     QAbstractFileEngine::create()).
0092 */
0093 
0094 //**************** QFSFileEnginePrivate
0095 QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate()
0096 {
0097     init();
0098 }
0099 
0100 /*!
0101     \internal
0102 */
0103 void QFSFileEnginePrivate::init()
0104 {
0105     is_sequential = 0;
0106     tried_stat = 0;
0107 #ifdef Q_OS_UNIX
0108     need_lstat = 1;
0109     is_link = 0;
0110 #endif
0111     openMode = QIODevice::NotOpen;
0112     fd = -1;
0113     fh = 0;
0114     lastIOCommand = IOFlushCommand;
0115     lastFlushFailed = false;
0116     closeFileHandle = false;
0117 #ifdef Q_OS_WIN
0118     fileAttrib = INVALID_FILE_ATTRIBUTES;
0119     fileHandle = INVALID_HANDLE_VALUE;
0120     cachedFd = -1;
0121 #endif
0122 #ifdef Q_USE_DEPRECATED_MAP_API
0123     fileMapHandle = INVALID_HANDLE_VALUE;
0124 #endif
0125 }
0126 
0127 /*!
0128     \internal
0129 
0130     Returns the canonicalized form of \a path (i.e., with all symlinks
0131     resolved, and all redundant path elements removed.
0132 */
0133 QString QFSFileEnginePrivate::canonicalized(const QString &path)
0134 {
0135     if (path.isEmpty())
0136         return path;
0137 
0138     QFileInfo fi;
0139     const QChar slash(QLatin1Char('/'));
0140     QString tmpPath = path;
0141     int separatorPos = 0;
0142     QSet<QString> nonSymlinks;
0143     QSet<QString> known;
0144 
0145     known.insert(path);
0146     do {
0147 #ifdef Q_OS_WIN
0148         // UNC, skip past the first two elements
0149         if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//")))
0150             separatorPos = tmpPath.indexOf(slash, 2);
0151         if (separatorPos != -1)
0152 #endif
0153         separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
0154         QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
0155         if (!nonSymlinks.contains(prefix)) {
0156             fi.setFile(prefix);
0157             if (fi.isSymLink()) {
0158                 QString target = fi.symLinkTarget();
0159                 if (separatorPos != -1) {
0160                     if (fi.isDir() && !target.endsWith(slash))
0161                         target.append(slash);
0162                     target.append(tmpPath.mid(separatorPos));
0163                 }
0164                 tmpPath = QDir::cleanPath(target);
0165                 separatorPos = 0;
0166 
0167                 if (known.contains(tmpPath))
0168                     return QString();
0169                 known.insert(tmpPath);
0170             } else {
0171                 nonSymlinks.insert(prefix);
0172             }
0173         }
0174     } while (separatorPos != -1);
0175 
0176     return QDir::cleanPath(tmpPath);
0177 }
0178 
0179 /*!
0180     Constructs a QFSFileEngine for the file name \a file.
0181 */
0182 QFSFileEngine::QFSFileEngine(const QString &file) : QAbstractFileEngine(*new QFSFileEnginePrivate)
0183 {
0184     Q_D(QFSFileEngine);
0185     d->filePath = QDir::fromNativeSeparators(file);
0186     d->nativeInitFileName();
0187 }
0188 
0189 /*!
0190     Constructs a QFSFileEngine.
0191 */
0192 QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate)
0193 {
0194 }
0195 
0196 /*!
0197     \internal
0198 */
0199 QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
0200     : QAbstractFileEngine(dd)
0201 {
0202 }
0203 
0204 /*!
0205     Destructs the QFSFileEngine.
0206 */
0207 QFSFileEngine::~QFSFileEngine()
0208 {
0209     Q_D(QFSFileEngine);
0210     if (d->closeFileHandle) {
0211         if (d->fh) {
0212             int ret;
0213             do {
0214                 ret = fclose(d->fh);
0215             } while (ret == EOF && errno == EINTR);
0216         } else if (d->fd != -1) {
0217             int ret;
0218             do {
0219                 ret = QT_CLOSE(d->fd);
0220             } while (ret == -1 && errno == EINTR);
0221         }
0222     }
0223     QList<uchar*> keys = d->maps.keys();
0224     for (int i = 0; i < keys.count(); ++i)
0225         unmap(keys.at(i));
0226 }
0227 
0228 /*!
0229     \reimp
0230 */
0231 void QFSFileEngine::setFileName(const QString &file)
0232 {
0233     Q_D(QFSFileEngine);
0234     d->init();
0235     d->filePath = QDir::fromNativeSeparators(file);
0236     d->nativeInitFileName();
0237 }
0238 
0239 /*!
0240     \reimp
0241 */
0242 bool QFSFileEngine::open(QIODevice::OpenMode openMode)
0243 {
0244     Q_D(QFSFileEngine);
0245     if (d->filePath.isEmpty()) {
0246         qWarning("QFSFileEngine::open: No file name specified");
0247         setError(QFile::OpenError, QLatin1String("No file name specified"));
0248         return false;
0249     }
0250 
0251     // Append implies WriteOnly.
0252     if (openMode & QFile::Append)
0253         openMode |= QFile::WriteOnly;
0254 
0255     // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
0256     if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
0257         openMode |= QFile::Truncate;
0258 
0259     d->openMode = openMode;
0260     d->lastFlushFailed = false;
0261     d->tried_stat = 0;
0262     d->fh = 0;
0263     d->fd = -1;
0264 
0265     return d->nativeOpen(openMode);
0266 }
0267 
0268 /*!
0269     Opens the file handle \a fh in \a openMode mode. Returns true on
0270     success; otherwise returns false.
0271 */
0272 bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
0273 {
0274     Q_D(QFSFileEngine);
0275 
0276     // Append implies WriteOnly.
0277     if (openMode & QFile::Append)
0278         openMode |= QFile::WriteOnly;
0279 
0280     // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
0281     if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
0282         openMode |= QFile::Truncate;
0283 
0284     d->openMode = openMode;
0285     d->lastFlushFailed = false;
0286     d->closeFileHandle = false;
0287     d->nativeFilePath.clear();
0288     d->filePath.clear();
0289     d->tried_stat = 0;
0290     d->fd = -1;
0291 
0292     return d->openFh(openMode, fh);
0293 }
0294 
0295 /*!
0296     Opens the file handle \a fh using the open mode \a flags.
0297 */
0298 bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
0299 {
0300     Q_Q(QFSFileEngine);
0301     this->fh = fh;
0302     fd = -1;
0303 
0304     // Seek to the end when in Append mode.
0305     if (openMode & QIODevice::Append) {
0306         int ret;
0307         do {
0308             ret = QT_FSEEK(fh, 0, SEEK_END);
0309         } while (ret == -1 && errno == EINTR);
0310 
0311         if (ret == -1) {
0312             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
0313                         qt_error_string(int(errno)));
0314             return false;
0315         }
0316     }
0317 
0318     return true;
0319 }
0320 
0321 /*!
0322     Opens the file descriptor \a fd in \a openMode mode. Returns true
0323     on success; otherwise returns false.
0324 */
0325 bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
0326 {
0327     Q_D(QFSFileEngine);
0328 
0329     // Append implies WriteOnly.
0330     if (openMode & QFile::Append)
0331         openMode |= QFile::WriteOnly;
0332 
0333     // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
0334     if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
0335         openMode |= QFile::Truncate;
0336 
0337     d->lastFlushFailed = false;
0338     d->closeFileHandle = false;
0339     d->nativeFilePath.clear();
0340     d->filePath.clear();
0341     d->fh = 0;
0342     d->fd = -1;
0343     d->tried_stat = 0;
0344 
0345     return d->openFd(openMode, fd);
0346 }
0347 
0348 
0349 /*!
0350     Opens the file descriptor \a fd to the file engine, using the open mode \a
0351     flags.
0352 */
0353 bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
0354 {
0355     Q_Q(QFSFileEngine);
0356     this->fd = fd;
0357     fh = 0;
0358 
0359     // Seek to the end when in Append mode.
0360     if (openMode & QFile::Append) {
0361         int ret;
0362         do {
0363             ret = QT_LSEEK(fd, 0, SEEK_END);
0364         } while (ret == -1 && errno == EINTR);
0365 
0366         if (ret == -1) {
0367             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
0368                         qt_error_string(int(errno)));
0369             return false;
0370         }
0371     }
0372 
0373     return true;
0374 }
0375 
0376 /*!
0377     \reimp
0378 */
0379 bool QFSFileEngine::close()
0380 {
0381     Q_D(QFSFileEngine);
0382     d->openMode = QIODevice::NotOpen;
0383     return d->nativeClose();
0384 }
0385 
0386 /*!
0387     \internal
0388 */
0389 bool QFSFileEnginePrivate::closeFdFh()
0390 {
0391     Q_Q(QFSFileEngine);
0392     if (fd == -1 && !fh)
0393         return false;
0394 
0395     // Flush the file if it's buffered, and if the last flush didn't fail.
0396     bool flushed = !fh || (!lastFlushFailed && q->flush());
0397     bool closed = true;
0398     tried_stat = 0;
0399 
0400     // Close the file if we created the handle.
0401     if (closeFileHandle) {
0402         int ret;
0403         do {
0404             if (fh) {
0405                 // Close buffered file.
0406                 ret = fclose(fh) != 0 ? -1 : 0;
0407             } else {
0408                 // Close unbuffered file.
0409                 ret = QT_CLOSE(fd);
0410             }
0411         } while (ret == -1 && errno == EINTR);
0412 
0413         // We must reset these guys regardless; calling close again after a
0414         // failed close causes crashes on some systems.
0415         fh = 0;
0416         fd = -1;
0417         closed = (ret == 0);
0418     }
0419 
0420     // Report errors.
0421     if (!flushed || !closed) {
0422         if (flushed) {
0423             // If not flushed, we want the flush error to fall through.
0424             q->setError(QFile::UnspecifiedError, qt_error_string(errno));
0425         }
0426         return false;
0427     }
0428 
0429     return true;
0430 }
0431 
0432 /*!
0433     \reimp
0434 */
0435 bool QFSFileEngine::flush()
0436 {
0437     Q_D(QFSFileEngine);
0438     if ((d->openMode & QIODevice::WriteOnly) == 0) {
0439         // Nothing in the write buffers, so flush succeeds in doing
0440         // nothing.
0441         return true;
0442     }
0443     return d->nativeFlush();
0444 }
0445 
0446 /*!
0447     \internal
0448 */
0449 bool QFSFileEnginePrivate::flushFh()
0450 {
0451     Q_Q(QFSFileEngine);
0452 
0453     // Never try to flush again if the last flush failed. Otherwise you can
0454     // get crashes on some systems (AIX).
0455     if (lastFlushFailed)
0456         return false;
0457 
0458     int ret = fflush(fh);
0459 
0460     lastFlushFailed = (ret != 0);
0461     lastIOCommand = QFSFileEnginePrivate::IOFlushCommand;
0462 
0463     if (ret != 0) {
0464         q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
0465                     qt_error_string(errno));
0466         return false;
0467     }
0468     return true;
0469 }
0470 
0471 /*!
0472     \reimp
0473 */
0474 qint64 QFSFileEngine::size() const
0475 {
0476     Q_D(const QFSFileEngine);
0477     return d->nativeSize();
0478 }
0479 
0480 /*!
0481     \internal
0482 */
0483 qint64 QFSFileEnginePrivate::sizeFdFh() const
0484 {
0485     Q_Q(const QFSFileEngine);
0486     // ### Fix this function, it should not stat unless the file is closed.
0487     QT_STATBUF st;
0488     int ret = 0;
0489     const_cast<QFSFileEngine *>(q)->flush();
0490     if (fh && nativeFilePath.isEmpty()) {
0491         // Buffered stdlib mode.
0492         // ### This should really be an ftell
0493         ret = QT_FSTAT(QT_FILENO(fh), &st);
0494     } else if (fd == -1) {
0495         // Stateless stat.
0496         ret = QT_STAT(nativeFilePath.constData(), &st);
0497     } else {
0498         // Unbuffered stdio mode.
0499         ret = QT_FSTAT(fd, &st);
0500     }
0501     if (ret == -1)
0502         return 0;
0503     return st.st_size;
0504 }
0505 
0506 /*!
0507     \reimp
0508 */
0509 qint64 QFSFileEngine::pos() const
0510 {
0511     Q_D(const QFSFileEngine);
0512     return d->nativePos();
0513 }
0514 
0515 /*!
0516     \internal
0517 */
0518 qint64 QFSFileEnginePrivate::posFdFh() const
0519 {
0520     if (fh)
0521         return qint64(QT_FTELL(fh));
0522     return QT_LSEEK(fd, 0, SEEK_CUR);
0523 }
0524 
0525 /*!
0526     \reimp
0527 */
0528 bool QFSFileEngine::seek(qint64 pos)
0529 {
0530     Q_D(QFSFileEngine);
0531     return d->nativeSeek(pos);
0532 }
0533 
0534 /*!
0535     \internal
0536 */
0537 bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
0538 {
0539     Q_Q(QFSFileEngine);
0540 
0541     // On Windows' stdlib implementation, the results of calling fread and
0542     // fwrite are undefined if not called either in sequence, or if preceded
0543     // with a call to fflush().
0544     if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
0545         return false;
0546 
0547     if (fh) {
0548         // Buffered stdlib mode.
0549         int ret;
0550         do {
0551             ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET);
0552         } while (ret == -1 && errno == EINTR);
0553 
0554         if (ret == -1) {
0555             q->setError(QFile::ReadError, qt_error_string(int(errno)));
0556             return false;
0557         }
0558     } else {
0559         // Unbuffered stdio mode.
0560         if (QT_LSEEK(fd, pos, SEEK_SET) == -1) {
0561             qWarning("QFile::at: Cannot set file position %lld", pos);
0562             q->setError(QFile::PositionError, qt_error_string(errno));
0563             return false;
0564         }
0565     }
0566     return true;
0567 }
0568 
0569 /*!
0570     \reimp
0571 */
0572 int QFSFileEngine::handle() const
0573 {
0574     Q_D(const QFSFileEngine);
0575     return d->nativeHandle();
0576 }
0577 
0578 /*!
0579     \reimp
0580 */
0581 qint64 QFSFileEngine::read(char *data, qint64 maxlen)
0582 {
0583     Q_D(QFSFileEngine);
0584 
0585     // On Windows' stdlib implementation, the results of calling fread and
0586     // fwrite are undefined if not called either in sequence, or if preceded
0587     // with a call to fflush().
0588     if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
0589         flush();
0590         d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
0591     }
0592 
0593     return d->nativeRead(data, maxlen);
0594 }
0595 
0596 /*!
0597     \internal
0598 */
0599 qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
0600 {
0601     Q_Q(QFSFileEngine);
0602 
0603     // Buffered stdlib mode.
0604     if (fh) {
0605         qint64 readBytes = 0;
0606         qint64 read = 0;
0607         int retry = 0;
0608 
0609         // Read in blocks of 4k to avoid platform limitations (Windows
0610         // commonly bails out if you read or write too large blocks at once).
0611         qint64 bytesToRead;
0612         do {
0613             if (retry == 1)
0614                 retry = 2;
0615 
0616             bytesToRead = qMin<qint64>(4096, len - read);
0617             do {
0618                 readBytes = fread(data + read, 1, size_t(bytesToRead), fh);
0619             } while (readBytes == 0 && !feof(fh) && errno == EINTR);
0620 
0621             if (readBytes > 0) {
0622                 read += readBytes;
0623             } else if (!retry && feof(fh)) {
0624                 // Synchronize and try again (just once though).
0625                 if (++retry == 1)
0626                     QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET);
0627             }
0628         } while (retry == 1 || (readBytes == bytesToRead && read < len));
0629 
0630         // Return the number of bytes read, or if nothing was read, return -1
0631         // if an error occurred, or 0 if we detected EOF.
0632         if (read == 0) {
0633             q->setError(QFile::ReadError, qt_error_string(int(errno)));
0634             if (!feof(fh))
0635                 read = -1;
0636         }
0637         return read;
0638     }
0639 
0640     // Unbuffered stdio mode.
0641     qint64 ret = 0;
0642     if (len) {
0643         int result;
0644         qint64 read = 0;
0645         errno = 0;
0646 
0647         // Read in blocks of 4k to avoid platform limitations (Windows
0648         // commonly bails out if you read or write too large blocks at once).
0649         do {
0650             qint64 bytesToRead = qMin<qint64>(4096, len - read);
0651             do {
0652                 result = QT_READ(fd, data + read, int(bytesToRead));
0653             } while (result == -1 && errno == EINTR);
0654             if (result > 0)
0655                 read += result;
0656         } while (result > 0 && read < len);
0657 
0658         // Return the number of bytes read, or if nothing was read, return -1
0659         // if an error occurred.
0660         if (read > 0) {
0661             ret += read;
0662         } else if (read == 0 && result < 0) {
0663             ret = -1;
0664             q->setError(QFile::ReadError, qt_error_string(errno));
0665         }
0666     }
0667     return ret;
0668 }
0669 
0670 /*!
0671     \reimp
0672 */
0673 qint64 QFSFileEngine::readLine(char *data, qint64 maxlen)
0674 {
0675     Q_D(QFSFileEngine);
0676 
0677     // On Windows' stdlib implementation, the results of calling fread and
0678     // fwrite are undefined if not called either in sequence, or if preceded
0679     // with a call to fflush().
0680     if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
0681         flush();
0682         d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
0683     }
0684 
0685     return d->nativeReadLine(data, maxlen);
0686 }
0687 
0688 /*!
0689     \internal
0690 */
0691 qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
0692 {
0693     Q_Q(QFSFileEngine);
0694     if (!fh)
0695         return q->QAbstractFileEngine::readLine(data, maxlen);
0696 
0697     QT_OFF_T oldPos = 0;
0698 #ifdef Q_OS_WIN
0699     bool seq = q->isSequential();
0700     if (!seq)
0701 #endif
0702         oldPos = QT_FTELL(fh);
0703 
0704     // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData()
0705     // because it has made space for the '\0' at the end of data.  But fgets
0706     // does the same, so we'd get two '\0' at the end - passing maxlen + 1
0707     // solves this.
0708     if (!fgets(data, int(maxlen + 1), fh)) {
0709         if (!feof(fh))
0710             q->setError(QFile::ReadError, qt_error_string(int(errno)));
0711         return -1;              // error
0712     }
0713 
0714 #ifdef Q_OS_WIN
0715     if (seq)
0716         return qstrlen(data);
0717 #endif
0718 
0719     qint64 lineLength = QT_FTELL(fh) - oldPos;
0720     return lineLength > 0 ? lineLength : qstrlen(data);
0721 }
0722 
0723 /*!
0724     \reimp
0725 */
0726 qint64 QFSFileEngine::write(const char *data, qint64 len)
0727 {
0728     Q_D(QFSFileEngine);
0729 
0730     // On Windows' stdlib implementation, the results of calling fread and
0731     // fwrite are undefined if not called either in sequence, or if preceded
0732     // with a call to fflush().
0733     if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) {
0734         flush();
0735         d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand;
0736     }
0737 
0738     return d->nativeWrite(data, len);
0739 }
0740 
0741 /*!
0742     \internal
0743 */
0744 qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
0745 {
0746     Q_Q(QFSFileEngine);
0747     qint64 result;
0748     qint64 written = 0;
0749 
0750     do {
0751         // Write blocks of 4k to avoid platform limitations (Windows commonly
0752         // bails out if you read or write too large blocks at once).
0753         qint64 bytesToWrite = qMin<qint64>(4096, len - written);
0754         if (fh) {
0755             do {
0756                 // Buffered stdlib mode.
0757                 result = qint64(fwrite(data + written, 1, size_t(bytesToWrite), fh));
0758             } while (result == 0 && errno == EINTR);
0759             if (bytesToWrite > 0 && result == 0)
0760                 result = -1;
0761         } else {
0762             do {
0763                 // Unbuffered stdio mode.
0764                 result = QT_WRITE(fd, data + written, bytesToWrite);
0765             } while (result == -1 && errno == EINTR);
0766         }
0767         if (result > 0)
0768             written += qint64(result);
0769     } while (written < len && result > 0);
0770 
0771     // If we read anything, return that with success. Otherwise, set an error,
0772     // and return the last return value.
0773     if (result > 0)
0774         return written;
0775     q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
0776     return result;
0777 }
0778 
0779 /*!
0780     \internal
0781 */
0782 QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
0783 {
0784     return new QFSFileEngineIterator(filters, filterNames);
0785 }
0786 
0787 /*!
0788     \internal
0789 */
0790 QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList()
0791 {
0792     return 0;
0793 }
0794 
0795 /*!
0796     \internal
0797 */
0798 QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
0799 {
0800     return QAbstractFileEngine::entryList(filters, filterNames);
0801 }
0802 
0803 /*!
0804     \reimp
0805 */
0806 bool QFSFileEngine::isSequential() const
0807 {
0808     Q_D(const QFSFileEngine);
0809     if (d->is_sequential == 0)
0810         d->is_sequential = d->nativeIsSequential() ? 1 : 2;
0811     return d->is_sequential == 1;
0812 }
0813 
0814 /*!
0815     \internal
0816 */
0817 bool QFSFileEnginePrivate::isSequentialFdFh() const
0818 {
0819     if (!tried_stat)
0820         doStat();
0821     if (could_stat) {
0822 #ifdef Q_OS_UNIX
0823         return (st.st_mode & S_IFMT) != S_IFREG;
0824         // ### WINDOWS!
0825 #endif
0826     }
0827     return true;
0828 }
0829 
0830 /*!
0831     \reimp
0832 */
0833 bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
0834 {
0835     Q_D(QFSFileEngine);
0836     if (extension == AtEndExtension && d->fh && isSequential())
0837         return feof(d->fh);
0838 
0839     if (extension == MapExtension) {
0840         const MapExtensionOption *options = (MapExtensionOption*)(option);
0841         MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
0842         returnValue->address = d->map(options->offset, options->size, options->flags);
0843         return (returnValue->address != 0);
0844     }
0845     if (extension == UnMapExtension) {
0846         UnMapExtensionOption *options = (UnMapExtensionOption*)option;
0847         return d->unmap(options->address);
0848     }
0849 
0850     return false;
0851 }
0852 
0853 /*!
0854     \reimp
0855 */
0856 bool QFSFileEngine::supportsExtension(Extension extension) const
0857 {
0858     Q_D(const QFSFileEngine);
0859     if (extension == AtEndExtension && d->fh && isSequential())
0860         return true;
0861     if (extension == FastReadLineExtension && d->fh)
0862         return true;
0863     if (extension == FastReadLineExtension && d->fd != -1 && isSequential())
0864         return true;
0865     if (extension == UnMapExtension || extension == MapExtension)
0866         return true;
0867     return false;
0868 }
0869 
0870 QT_END_NAMESPACE
0871 
0872 #endif // QT_NO_FSFILEENGINE