source: trunk/src/corelib/io/qfsfileengine.cpp @ 923

Last change on this file since 923 was 923, checked in by Dmitry A. Kuminov, 9 years ago

OS/2: Fix QFile::atEnd() returning 0 on a pipe after it is closed.

This is to be compatible with other platforms (e.g. Linux) and with
software that incorrectly uses the while(file.atEnd()) loop to read
data from a file.

File size: 27.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file.  Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights.  These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file.  Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qfsfileengine_p.h"
43#include "qfsfileengine_iterator_p.h"
44#include "qdatetime.h"
45#include "qdiriterator.h"
46#include "qset.h"
47#include <QtCore/qdebug.h>
48
49#ifndef QT_NO_FSFILEENGINE
50
51#if !defined(Q_OS_WINCE)
52#include <errno.h>
53#endif
54#if defined(Q_OS_UNIX)
55#include "private/qcore_unix_p.h"
56#endif
57#include <stdio.h>
58#include <stdlib.h>
59#if defined(Q_OS_MAC)
60# include <private/qcore_mac_p.h>
61#endif
62
63QT_BEGIN_NAMESPACE
64
65#ifdef Q_OS_WIN
66#  ifndef S_ISREG
67#    define S_ISREG(x)   (((x) & S_IFMT) == S_IFREG)
68#  endif
69#  ifndef S_ISCHR
70#    define S_ISCHR(x)   (((x) & S_IFMT) == S_IFCHR)
71#  endif
72#  ifndef S_ISFIFO
73#    define S_ISFIFO(x) false
74#  endif
75#  ifndef S_ISSOCK
76#    define S_ISSOCK(x) false
77#  endif
78#  ifndef INVALID_FILE_ATTRIBUTES
79#    define INVALID_FILE_ATTRIBUTES (DWORD (-1))
80#  endif
81#endif
82
83/*! \class QFSFileEngine
84    \brief The QFSFileEngine class implements Qt's default file engine.
85    \since 4.1
86
87    This class is part of the file engine framework in Qt. If you only want to
88    access files or directories, use QFile, QFileInfo or QDir instead.
89
90    QFSFileEngine is the default file engine for accessing regular files. It
91    is provided for convenience; by subclassing this class, you can alter its
92    behavior slightly, without having to write a complete QAbstractFileEngine
93    subclass. To install your custom file engine, you must also subclass
94    QAbstractFileEngineHandler and create an instance of your handler.
95
96    It can also be useful to create a QFSFileEngine object directly if you
97    need to use the local file system inside QAbstractFileEngine::create(), in
98    order to avoid recursion (as higher-level classes tend to call
99    QAbstractFileEngine::create()).
100*/
101
102//**************** QFSFileEnginePrivate
103QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate()
104{
105    init();
106}
107
108/*!
109    \internal
110*/
111void QFSFileEnginePrivate::init()
112{
113    is_sequential = 0;
114    tried_stat = 0;
115#if !defined(Q_OS_WINCE)
116    need_lstat = 1;
117    is_link = 0;
118#endif
119    openMode = QIODevice::NotOpen;
120    fd = -1;
121    fh = 0;
122    lastIOCommand = IOFlushCommand;
123    lastFlushFailed = false;
124    closeFileHandle = false;
125#ifdef Q_OS_WIN
126    fileAttrib = INVALID_FILE_ATTRIBUTES;
127    fileHandle = INVALID_HANDLE_VALUE;
128    mapHandle = INVALID_HANDLE_VALUE;
129#ifndef Q_OS_WINCE
130    cachedFd = -1;
131#endif
132#endif
133}
134
135/*!
136    \internal
137
138    Returns the canonicalized form of \a path (i.e., with all symlinks
139    resolved, and all redundant path elements removed.
140*/
141QString QFSFileEnginePrivate::canonicalized(const QString &path)
142{
143    if (path.isEmpty())
144        return path;
145
146    // FIXME let's see if this stuff works, then we might be able to remove some of the other code.
147#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
148    if (path.size() == 1 && path.at(0) == QLatin1Char('/'))
149        return path;
150#endif
151#if defined(Q_OS_LINUX) || defined(Q_OS_SYMBIAN) || defined(Q_OS_MAC)
152    // ... but Linux with uClibc does not have it
153#if !defined(__UCLIBC__)
154    char *ret = 0;
155#if defined(Q_OS_MAC)
156    // Mac OS X 10.5.x doesn't support the realpath(X,0) extension we use here.
157    if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) {
158        ret = realpath(path.toLocal8Bit().constData(), (char*)0);
159    } else {
160        // on 10.5 we can use FSRef to resolve the file path.
161        FSRef fsref;
162        if (FSPathMakeRef((const UInt8 *)QDir::cleanPath(path).toUtf8().data(), &fsref, 0) == noErr) {
163            CFURLRef urlref = CFURLCreateFromFSRef(NULL, &fsref);
164            CFStringRef canonicalPath = CFURLCopyFileSystemPath(urlref, kCFURLPOSIXPathStyle);
165            QString ret = QCFString::toQString(canonicalPath);
166            CFRelease(canonicalPath);
167            CFRelease(urlref);
168            return ret;
169        }
170    }
171#else
172    ret = realpath(path.toLocal8Bit().constData(), (char*)0);
173#endif
174    if (ret) {
175        QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret));
176        free(ret);
177        return canonicalPath;
178    }
179#endif
180#endif
181
182    QFileInfo fi;
183    const QChar slash(QLatin1Char('/'));
184    QString tmpPath = path;
185    int separatorPos = 0;
186    QSet<QString> nonSymlinks;
187    QSet<QString> known;
188
189    known.insert(path);
190    do {
191#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
192        // UNC, skip past the first two elements
193        if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//")))
194            separatorPos = tmpPath.indexOf(slash, 2);
195        if (separatorPos != -1)
196#endif
197        separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
198        QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
199        if (
200#ifdef Q_OS_SYMBIAN
201            // Symbian doesn't support directory symlinks, so do not check for link unless we
202            // are handling the last path element. This not only slightly improves performance,
203            // but also saves us from lot of unnecessary platform security check failures
204            // when dealing with files under *:/private directories.
205            separatorPos == -1 &&
206#endif
207            !nonSymlinks.contains(prefix)) {
208            fi.setFile(prefix);
209            if (fi.isSymLink()) {
210                QString target = fi.symLinkTarget();
211                if (separatorPos != -1) {
212                    if (fi.isDir() && !target.endsWith(slash))
213                        target.append(slash);
214                    target.append(tmpPath.mid(separatorPos));
215                }
216                tmpPath = QDir::cleanPath(target);
217                separatorPos = 0;
218
219                if (known.contains(tmpPath))
220                    return QString();
221                known.insert(tmpPath);
222            } else {
223                nonSymlinks.insert(prefix);
224            }
225        }
226    } while (separatorPos != -1);
227
228    return QDir::cleanPath(tmpPath);
229}
230
231/*!
232    Constructs a QFSFileEngine for the file name \a file.
233*/
234QFSFileEngine::QFSFileEngine(const QString &file) : QAbstractFileEngine(*new QFSFileEnginePrivate)
235{
236    Q_D(QFSFileEngine);
237    d->filePath = QDir::fromNativeSeparators(file);
238    d->nativeInitFileName();
239}
240
241/*!
242    Constructs a QFSFileEngine.
243*/
244QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate)
245{
246}
247
248/*!
249    \internal
250*/
251QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
252    : QAbstractFileEngine(dd)
253{
254}
255
256/*!
257    Destructs the QFSFileEngine.
258*/
259QFSFileEngine::~QFSFileEngine()
260{
261    Q_D(QFSFileEngine);
262    if (d->closeFileHandle) {
263        if (d->fh) {
264            int ret;
265            do {
266                ret = fclose(d->fh);
267            } while (ret == EOF && errno == EINTR);
268        } else if (d->fd != -1) {
269            int ret;
270            do {
271                ret = QT_CLOSE(d->fd);
272            } while (ret == -1 && errno == EINTR);
273        }
274    }
275    QList<uchar*> keys = d->maps.keys();
276    for (int i = 0; i < keys.count(); ++i)
277        unmap(keys.at(i));
278}
279
280/*!
281    \reimp
282*/
283void QFSFileEngine::setFileName(const QString &file)
284{
285    Q_D(QFSFileEngine);
286    d->init();
287    d->filePath = QDir::fromNativeSeparators(file);
288    d->nativeInitFileName();
289}
290
291/*!
292    \reimp
293*/
294bool QFSFileEngine::open(QIODevice::OpenMode openMode)
295{
296    Q_D(QFSFileEngine);
297    if (d->filePath.isEmpty()) {
298        qWarning("QFSFileEngine::open: No file name specified");
299        setError(QFile::OpenError, QLatin1String("No file name specified"));
300        return false;
301    }
302
303    // Append implies WriteOnly.
304    if (openMode & QFile::Append)
305        openMode |= QFile::WriteOnly;
306
307    // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
308    if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
309        openMode |= QFile::Truncate;
310
311    d->openMode = openMode;
312    d->lastFlushFailed = false;
313    d->tried_stat = 0;
314    d->fh = 0;
315    d->fd = -1;
316
317    return d->nativeOpen(openMode);
318}
319
320/*!
321    Opens the file handle \a fh in \a openMode mode. Returns true on
322    success; otherwise returns false.
323*/
324bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
325{
326    Q_D(QFSFileEngine);
327
328    // Append implies WriteOnly.
329    if (openMode & QFile::Append)
330        openMode |= QFile::WriteOnly;
331
332    // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
333    if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
334        openMode |= QFile::Truncate;
335
336    d->openMode = openMode;
337    d->lastFlushFailed = false;
338    d->closeFileHandle = false;
339    d->nativeFilePath.clear();
340    d->filePath.clear();
341    d->tried_stat = 0;
342    d->fd = -1;
343
344    return d->openFh(openMode, fh);
345}
346
347/*!
348    Opens the file handle \a fh using the open mode \a flags.
349*/
350bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
351{
352    Q_Q(QFSFileEngine);
353    this->fh = fh;
354    fd = -1;
355
356    // Seek to the end when in Append mode.
357    if (openMode & QIODevice::Append) {
358        int ret;
359        do {
360            ret = QT_FSEEK(fh, 0, SEEK_END);
361        } while (ret != 0 && errno == EINTR);
362
363        if (ret != 0) {
364            q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
365                        qt_error_string(int(errno)));
366
367            this->openMode = QIODevice::NotOpen;
368            this->fh = 0;
369
370            return false;
371        }
372    }
373
374    return true;
375}
376
377/*!
378    Opens the file descriptor \a fd in \a openMode mode. Returns true
379    on success; otherwise returns false.
380*/
381bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
382{
383    Q_D(QFSFileEngine);
384
385    // Append implies WriteOnly.
386    if (openMode & QFile::Append)
387        openMode |= QFile::WriteOnly;
388
389    // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
390    if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
391        openMode |= QFile::Truncate;
392
393    d->openMode = openMode;
394    d->lastFlushFailed = false;
395    d->closeFileHandle = false;
396    d->nativeFilePath.clear();
397    d->filePath.clear();
398    d->fh = 0;
399    d->fd = -1;
400    d->tried_stat = 0;
401
402    return d->openFd(openMode, fd);
403}
404
405
406/*!
407    Opens the file descriptor \a fd to the file engine, using the open mode \a
408    flags.
409*/
410bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
411{
412    Q_Q(QFSFileEngine);
413    this->fd = fd;
414    fh = 0;
415
416    // Seek to the end when in Append mode.
417    if (openMode & QFile::Append) {
418        int ret;
419        do {
420            ret = QT_LSEEK(fd, 0, SEEK_END);
421        } while (ret == -1 && errno == EINTR);
422
423        if (ret == -1) {
424            q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
425                        qt_error_string(int(errno)));
426
427            this->openMode = QIODevice::NotOpen;
428            this->fd = -1;
429
430            return false;
431        }
432    }
433
434    return true;
435}
436
437/*!
438    \reimp
439*/
440bool QFSFileEngine::close()
441{
442    Q_D(QFSFileEngine);
443    d->openMode = QIODevice::NotOpen;
444    return d->nativeClose();
445}
446
447/*!
448    \internal
449*/
450bool QFSFileEnginePrivate::closeFdFh()
451{
452    Q_Q(QFSFileEngine);
453    if (fd == -1 && !fh)
454        return false;
455
456    // Flush the file if it's buffered, and if the last flush didn't fail.
457    bool flushed = !fh || (!lastFlushFailed && q->flush());
458    bool closed = true;
459    tried_stat = 0;
460
461    // Close the file if we created the handle.
462    if (closeFileHandle) {
463        int ret;
464        do {
465            if (fh) {
466                // Close buffered file.
467                ret = fclose(fh) != 0 ? -1 : 0;
468            } else {
469                // Close unbuffered file.
470                ret = QT_CLOSE(fd);
471            }
472        } while (ret == -1 && errno == EINTR);
473
474        // We must reset these guys regardless; calling close again after a
475        // failed close causes crashes on some systems.
476        fh = 0;
477        fd = -1;
478        closed = (ret == 0);
479    }
480
481    // Report errors.
482    if (!flushed || !closed) {
483        if (flushed) {
484            // If not flushed, we want the flush error to fall through.
485            q->setError(QFile::UnspecifiedError, qt_error_string(errno));
486        }
487        return false;
488    }
489
490    return true;
491}
492
493/*!
494    \reimp
495*/
496bool QFSFileEngine::flush()
497{
498    Q_D(QFSFileEngine);
499    if ((d->openMode & QIODevice::WriteOnly) == 0) {
500        // Nothing in the write buffers, so flush succeeds in doing
501        // nothing.
502        return true;
503    }
504    return d->nativeFlush();
505}
506
507/*!
508    \internal
509*/
510bool QFSFileEnginePrivate::flushFh()
511{
512    Q_Q(QFSFileEngine);
513
514    // Never try to flush again if the last flush failed. Otherwise you can
515    // get crashes on some systems (AIX).
516    if (lastFlushFailed)
517        return false;
518
519    int ret = fflush(fh);
520
521    lastFlushFailed = (ret != 0);
522    lastIOCommand = QFSFileEnginePrivate::IOFlushCommand;
523
524    if (ret != 0) {
525        q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
526                    qt_error_string(errno));
527        return false;
528    }
529    return true;
530}
531
532/*!
533    \reimp
534*/
535qint64 QFSFileEngine::size() const
536{
537    Q_D(const QFSFileEngine);
538    return d->nativeSize();
539}
540
541/*!
542    \internal
543*/
544qint64 QFSFileEnginePrivate::sizeFdFh() const
545{
546    Q_Q(const QFSFileEngine);
547    // ### Fix this function, it should not stat unless the file is closed.
548    QT_STATBUF st;
549    int ret = 0;
550    const_cast<QFSFileEngine *>(q)->flush();
551    if (fh && nativeFilePath.isEmpty()) {
552        // Buffered stdlib mode.
553        // ### This should really be an ftell
554        ret = QT_FSTAT(QT_FILENO(fh), &st);
555    } else if (fd == -1) {
556        // Stateless stat.
557        ret = QT_STAT(nativeFilePath.constData(), &st);
558    } else {
559        // Unbuffered stdio mode.
560        ret = QT_FSTAT(fd, &st);
561    }
562    if (ret == -1)
563        return 0;
564    return st.st_size;
565}
566
567/*!
568    \reimp
569*/
570qint64 QFSFileEngine::pos() const
571{
572    Q_D(const QFSFileEngine);
573    return d->nativePos();
574}
575
576/*!
577    \internal
578*/
579qint64 QFSFileEnginePrivate::posFdFh() const
580{
581    if (fh)
582        return qint64(QT_FTELL(fh));
583    return QT_LSEEK(fd, 0, SEEK_CUR);
584}
585
586/*!
587    \reimp
588*/
589bool QFSFileEngine::seek(qint64 pos)
590{
591    Q_D(QFSFileEngine);
592    return d->nativeSeek(pos);
593}
594
595/*!
596    \internal
597*/
598bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
599{
600    Q_Q(QFSFileEngine);
601
602    // On Windows' stdlib implementation, the results of calling fread and
603    // fwrite are undefined if not called either in sequence, or if preceded
604    // with a call to fflush().
605    if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
606        return false;
607
608    if (pos < 0 || pos != qint64(QT_OFF_T(pos)))
609        return false;
610
611    if (fh) {
612        // Buffered stdlib mode.
613        int ret;
614        do {
615            ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET);
616        } while (ret != 0 && errno == EINTR);
617
618        if (ret != 0) {
619            q->setError(QFile::ReadError, qt_error_string(int(errno)));
620            return false;
621        }
622    } else {
623        // Unbuffered stdio mode.
624        if (QT_LSEEK(fd, QT_OFF_T(pos), SEEK_SET) == -1) {
625            qWarning() << "QFile::at: Cannot set file position" << pos;
626            q->setError(QFile::PositionError, qt_error_string(errno));
627            return false;
628        }
629    }
630    return true;
631}
632
633/*!
634    \reimp
635*/
636int QFSFileEngine::handle() const
637{
638    Q_D(const QFSFileEngine);
639    return d->nativeHandle();
640}
641
642/*!
643    \reimp
644*/
645qint64 QFSFileEngine::read(char *data, qint64 maxlen)
646{
647    Q_D(QFSFileEngine);
648
649    // On Windows' stdlib implementation, the results of calling fread and
650    // fwrite are undefined if not called either in sequence, or if preceded
651    // with a call to fflush().
652    if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
653        flush();
654        d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
655    }
656
657    return d->nativeRead(data, maxlen);
658}
659
660/*!
661    \internal
662*/
663qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
664{
665    Q_Q(QFSFileEngine);
666
667    if (len < 0 || len != qint64(size_t(len))) {
668        q->setError(QFile::ReadError, qt_error_string(EINVAL));
669        return -1;
670    }
671
672    qint64 readBytes = 0;
673    bool eof = false;
674
675    if (fh) {
676        // Buffered stdlib mode.
677
678        size_t result;
679        bool retry = true;
680        do {
681            result = fread(data + readBytes, 1, size_t(len - readBytes), fh);
682            eof = feof(fh);
683            if (retry && eof && result == 0) {
684                // On Mac OS, this is needed, e.g., if a file was written to
685                // through another stream since our last read. See test
686                // tst_QFile::appendAndRead
687                QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET); // re-sync stream.
688                retry = false;
689                continue;
690            }
691            readBytes += result;
692        } while (!eof && (result == 0 ? errno == EINTR : readBytes < len));
693
694    } else if (fd != -1) {
695        // Unbuffered stdio mode.
696
697#ifdef Q_OS_WIN
698        int result;
699#else
700        ssize_t result;
701#endif
702        do {
703            result = QT_READ(fd, data + readBytes, size_t(len - readBytes));
704        } while ((result == -1 && errno == EINTR)
705                || (result > 0 && (readBytes += result) < len));
706
707        eof = !(result == -1);
708    }
709
710    if (!eof && readBytes == 0) {
711        readBytes = -1;
712        q->setError(QFile::ReadError, qt_error_string(errno));
713    }
714
715    return readBytes;
716}
717
718/*!
719    \reimp
720*/
721qint64 QFSFileEngine::readLine(char *data, qint64 maxlen)
722{
723    Q_D(QFSFileEngine);
724
725    // On Windows' stdlib implementation, the results of calling fread and
726    // fwrite are undefined if not called either in sequence, or if preceded
727    // with a call to fflush().
728    if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
729        flush();
730        d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
731    }
732
733    return d->nativeReadLine(data, maxlen);
734}
735
736/*!
737    \internal
738*/
739qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
740{
741    Q_Q(QFSFileEngine);
742    if (!fh)
743        return q->QAbstractFileEngine::readLine(data, maxlen);
744
745    QT_OFF_T oldPos = 0;
746#ifdef Q_OS_WIN
747    bool seq = q->isSequential();
748    if (!seq)
749#endif
750        oldPos = QT_FTELL(fh);
751
752    // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData()
753    // because it has made space for the '\0' at the end of data.  But fgets
754    // does the same, so we'd get two '\0' at the end - passing maxlen + 1
755    // solves this.
756    if (!fgets(data, int(maxlen + 1), fh)) {
757        if (!feof(fh))
758            q->setError(QFile::ReadError, qt_error_string(int(errno)));
759        return -1;              // error
760    }
761
762#ifdef Q_OS_WIN
763    if (seq)
764        return qstrlen(data);
765#endif
766
767    qint64 lineLength = QT_FTELL(fh) - oldPos;
768    return lineLength > 0 ? lineLength : qstrlen(data);
769}
770
771/*!
772    \reimp
773*/
774qint64 QFSFileEngine::write(const char *data, qint64 len)
775{
776    Q_D(QFSFileEngine);
777
778    // On Windows' stdlib implementation, the results of calling fread and
779    // fwrite are undefined if not called either in sequence, or if preceded
780    // with a call to fflush().
781    if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) {
782        flush();
783        d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand;
784    }
785
786    return d->nativeWrite(data, len);
787}
788
789/*!
790    \internal
791*/
792qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
793{
794    Q_Q(QFSFileEngine);
795
796    if (len < 0 || len != qint64(size_t(len))) {
797        q->setError(QFile::WriteError, qt_error_string(EINVAL));
798        return -1;
799    }
800
801    qint64 writtenBytes = 0;
802
803    if (fh) {
804        // Buffered stdlib mode.
805
806        size_t result;
807        do {
808            result = fwrite(data + writtenBytes, 1, size_t(len - writtenBytes), fh);
809            writtenBytes += result;
810        } while (result == 0 ? errno == EINTR : writtenBytes < len);
811
812    } else if (fd != -1) {
813        // Unbuffered stdio mode.
814
815#ifdef Q_OS_WIN
816        int result;
817#else
818        ssize_t result;
819#endif
820        do {
821            result = QT_WRITE(fd, data + writtenBytes, size_t(len - writtenBytes));
822        } while ((result == -1 && errno == EINTR)
823                || (result > 0 && (writtenBytes += result) < len));
824    }
825
826    if (len &&  writtenBytes == 0) {
827        writtenBytes = -1;
828        q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
829    }
830
831    return writtenBytes;
832}
833
834/*!
835    \internal
836*/
837QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
838{
839    return new QFSFileEngineIterator(filters, filterNames);
840}
841
842/*!
843    \internal
844*/
845QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList()
846{
847    return 0;
848}
849
850/*!
851    \internal
852*/
853QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
854{
855    return QAbstractFileEngine::entryList(filters, filterNames);
856}
857
858/*!
859    \reimp
860*/
861bool QFSFileEngine::isSequential() const
862{
863    Q_D(const QFSFileEngine);
864    if (d->is_sequential == 0)
865        d->is_sequential = d->nativeIsSequential() ? 1 : 2;
866    return d->is_sequential == 1;
867}
868
869/*!
870    \internal
871*/
872bool QFSFileEnginePrivate::isSequentialFdFh() const
873{
874    if (!tried_stat)
875        doStat();
876    if (could_stat) {
877#if defined(Q_OS_UNIX) || defined(Q_OS_OS2)
878        return (st.st_mode & S_IFMT) != S_IFREG;
879        // ### WINDOWS!
880#endif
881    }
882    return true;
883}
884
885/*!
886    \reimp
887*/
888bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
889{
890    Q_D(QFSFileEngine);
891    if (extension == AtEndExtension && d->fh && isSequential())
892#ifdef Q_OS_OS2
893        // On OS/2 feof() doesn't return 1 when the stream is a pipe and the
894        // other end of the pipe gets closed. This makes programs using the
895        // somewhat incorrect "while (qfile.atEnd())..." loop spin forever (UIC
896        // is one of such programs). In order to fix it, we use ferror() which
897        // does return 1 in cases like that.
898        return feof(d->fh) || ferror(d->fh);
899#else
900        return feof(d->fh);
901#endif
902
903    if (extension == MapExtension) {
904        const MapExtensionOption *options = (MapExtensionOption*)(option);
905        MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
906        returnValue->address = d->map(options->offset, options->size, options->flags);
907        return (returnValue->address != 0);
908    }
909    if (extension == UnMapExtension) {
910        UnMapExtensionOption *options = (UnMapExtensionOption*)option;
911        return d->unmap(options->address);
912    }
913
914    return false;
915}
916
917/*!
918    \reimp
919*/
920bool QFSFileEngine::supportsExtension(Extension extension) const
921{
922    Q_D(const QFSFileEngine);
923    if (extension == AtEndExtension && d->fh && isSequential())
924        return true;
925    if (extension == FastReadLineExtension && d->fh)
926        return true;
927    if (extension == FastReadLineExtension && d->fd != -1 && isSequential())
928        return true;
929    if (extension == UnMapExtension || extension == MapExtension)
930        return true;
931    return false;
932}
933
934/*! \fn bool QFSFileEngine::caseSensitive() const
935  Returns true for Windows, false for Unix.
936*/
937
938/*! \fn bool QFSFileEngine::copy(const QString &copyName)
939
940  For windows, copy the file to file \a copyName.
941
942  Not implemented for Unix.
943*/
944
945/*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
946  For Unix, returns the current working directory for the file
947  engine.
948
949  For Windows and OS/2, returns the canonicalized form of the current path used
950  by the file engine for the drive specified by \a fileName.  On Windows and on
951  OS/2, each drive has its own current directory, so a different path is
952  returned for file names that include different drive names (e.g. A: or C:).
953
954  \sa setCurrentPath()
955*/
956
957/*! \fn QFileInfoList QFSFileEngine::drives()
958  For Windows, returns the list of drives in the file system as a list
959  of QFileInfo objects. On unix, Mac OS X and Windows CE, only the
960  root path is returned.  On Windows, this function returns all drives
961  (A:\, C:\, D:\, etc.).
962
963  For Unix, the list contains just the root path "/".
964*/
965
966/*! \fn QString QFSFileEngine::fileName(FileName file) const
967  \reimp
968*/
969
970/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const
971  \reimp
972*/
973
974/*! \fn QString QFSFileEngine::homePath()
975  Returns the home path of the current user.
976
977  \sa rootPath()
978*/
979
980/*! \fn bool QFSFileEngine::isRelativePath() const
981  \reimp
982*/
983
984/*! \fn bool QFSFileEngine::link(const QString &newName)
985
986  Creates a link from the file currently specified by fileName() to
987  \a newName. What a link is depends on the underlying filesystem
988  (be it a shortcut on Windows or a symbolic link on Unix). Returns
989  true if successful; otherwise returns false.
990*/
991
992/*! \fn bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
993  \reimp
994*/
995
996/*! \fn uint QFSFileEngine::ownerId(FileOwner own) const
997  In Unix, if stat() is successful, the \c uid is returned if
998  \a own is the owner. Otherwise the \c gid is returned. If stat()
999  is unsuccessful, -2 is reuturned.
1000
1001  For Windows, -2 is always returned.
1002*/
1003
1004/*! \fn QString QFSFileEngine::owner(FileOwner own) const
1005  \reimp
1006*/
1007
1008/*! \fn bool QFSFileEngine::remove()
1009  \reimp
1010*/
1011
1012/*! \fn bool QFSFileEngine::rename(const QString &newName)
1013  \reimp
1014*/
1015
1016/*! \fn bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
1017  \reimp
1018*/
1019
1020/*! \fn QString QFSFileEngine::rootPath()
1021  Returns the root path.
1022
1023  \sa homePath()
1024*/
1025
1026/*! \fn bool QFSFileEngine::setCurrentPath(const QString &path)
1027  Sets the current path (e.g., for QDir), to \a path. Returns true if the
1028  new path exists; otherwise this function does nothing, and returns false.
1029
1030  \sa currentPath()
1031*/
1032
1033/*! \fn bool QFSFileEngine::setPermissions(uint perms)
1034  \reimp
1035*/
1036
1037/*! \fn bool QFSFileEngine::setSize(qint64 size)
1038  \reimp
1039*/
1040
1041/*! \fn QString QFSFileEngine::tempPath()
1042  Returns the temporary path (i.e., a path in which it is safe
1043  to store temporary files).
1044*/
1045
1046/*! \fn QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const
1047    \internal
1048*/
1049
1050QT_END_NAMESPACE
1051
1052#endif // QT_NO_FSFILEENGINE
Note: See TracBrowser for help on using the repository browser.