summaryrefslogtreecommitdiffstats
path: root/src/tools/qfile_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qfile_unix.cpp')
-rw-r--r--src/tools/qfile_unix.cpp746
1 files changed, 746 insertions, 0 deletions
diff --git a/src/tools/qfile_unix.cpp b/src/tools/qfile_unix.cpp
new file mode 100644
index 0000000..b6da559
--- /dev/null
+++ b/src/tools/qfile_unix.cpp
@@ -0,0 +1,746 @@
+/****************************************************************************
+**
+** Implementation of QFile class
+**
+** Created : 950628
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the tools module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qplatformdefs.h"
+
+// POSIX Large File Support redefines open -> open64
+static inline int qt_open(const char *pathname, int flags, mode_t mode)
+{ return ::open(pathname, flags, mode); }
+#if defined(open)
+# undef open
+#endif
+
+// POSIX Large File Support redefines truncate -> truncate64
+#if defined(truncate)
+# undef truncate
+#endif
+
+#include "qfile.h"
+#include <errno.h>
+#include <limits.h>
+
+extern const char* qt_fileerr_read;
+
+bool qt_file_access( const QString& fn, int t )
+{
+ if ( fn.isEmpty() )
+ return FALSE;
+ return ::access( QFile::encodeName(fn), t ) == 0;
+}
+
+/*!
+ \overload
+ Removes the file \a fileName.
+ Returns TRUE if successful, otherwise FALSE.
+*/
+
+bool QFile::remove( const QString &fileName )
+{
+ if ( fileName.isEmpty() ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "QFile::remove: Empty or null file name" );
+#endif
+ return FALSE;
+ }
+ return unlink( QFile::encodeName(fileName) ) == 0;
+}
+
+#if defined(O_NONBLOCK)
+# define HAS_ASYNC_FILEMODE
+# define OPEN_ASYNC O_NONBLOCK
+#elif defined(O_NDELAY)
+# define HAS_ASYNC_FILEMODE
+# define OPEN_ASYNC O_NDELAY
+#endif
+
+/*!
+ Opens the file specified by the file name currently set, using the
+ mode \a m. Returns TRUE if successful, otherwise FALSE.
+
+ \keyword IO_Raw
+ \keyword IO_ReadOnly
+ \keyword IO_WriteOnly
+ \keyword IO_ReadWrite
+ \keyword IO_Append
+ \keyword IO_Truncate
+ \keyword IO_Translate
+
+ The mode parameter \a m must be a combination of the following flags:
+ \table
+ \header \i Flag \i Meaning
+ \row \i IO_Raw
+ \i Raw (non-buffered) file access.
+ \row \i IO_ReadOnly
+ \i Opens the file in read-only mode.
+ \row \i IO_WriteOnly
+ \i Opens the file in write-only mode. If this flag is used
+ with another flag, e.g. \c IO_ReadOnly or \c IO_Raw or \c
+ IO_Append, the file is \e not truncated; but if used on
+ its own (or with \c IO_Truncate), the file is truncated.
+ \row \i IO_ReadWrite
+ \i Opens the file in read/write mode, equivalent to \c
+ (IO_ReadOnly | IO_WriteOnly).
+ \row \i IO_Append
+ \i Opens the file in append mode. (You must actually use \c
+ (IO_WriteOnly | IO_Append) to make the file writable and
+ to go into append mode.) This mode is very useful when you
+ want to write something to a log file. The file index is
+ set to the end of the file. Note that the result is
+ undefined if you position the file index manually using
+ at() in append mode.
+ \row \i IO_Truncate
+ \i Truncates the file.
+ \row \i IO_Translate
+ \i Enables carriage returns and linefeed translation for text
+ files under Windows.
+ \endtable
+
+ The raw access mode is best when I/O is block-operated using a 4KB
+ block size or greater. Buffered access works better when reading
+ small portions of data at a time.
+
+ \warning When working with buffered files, data may not be written
+ to the file at once. Call flush() to make sure that the data is
+ really written.
+
+ \warning If you have a buffered file opened for both reading and
+ writing you must not perform an input operation immediately after
+ an output operation or vice versa. You should always call flush()
+ or a file positioning operation, e.g. at(), between input and
+ output operations, otherwise the buffer may contain garbage.
+
+ If the file does not exist and \c IO_WriteOnly or \c IO_ReadWrite
+ is specified, it is created.
+
+ Example:
+ \code
+ QFile f1( "/tmp/data.bin" );
+ f1.open( IO_Raw | IO_ReadWrite );
+
+ QFile f2( "readme.txt" );
+ f2.open( IO_ReadOnly | IO_Translate );
+
+ QFile f3( "audit.log" );
+ f3.open( IO_WriteOnly | IO_Append );
+ \endcode
+
+ \sa name(), close(), isOpen(), flush()
+*/
+
+bool QFile::open( int m )
+{
+ if ( isOpen() ) { // file already open
+#if defined(QT_CHECK_STATE)
+ qWarning( "QFile::open: File already open" );
+#endif
+ return FALSE;
+ }
+ if ( fn.isEmpty() ) { // no file name defined
+#if defined(QT_CHECK_NULL)
+ qWarning( "QFile::open: No file name specified" );
+#endif
+ return FALSE;
+ }
+ init(); // reset params
+ setMode( m );
+ if ( !(isReadable() || isWritable()) ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QFile::open: File access not specified" );
+#endif
+ return FALSE;
+ }
+ bool ok = TRUE;
+ struct stat st;
+ if ( isRaw() ) {
+ int oflags = O_RDONLY;
+ if ( isReadable() && isWritable() )
+ oflags = O_RDWR;
+ else if ( isWritable() )
+ oflags = O_WRONLY;
+ if ( flags() & IO_Append ) { // append to end of file?
+ if ( flags() & IO_Truncate )
+ oflags |= (O_CREAT | O_TRUNC);
+ else
+ oflags |= (O_APPEND | O_CREAT);
+ setFlags( flags() | IO_WriteOnly ); // append implies write
+ } else if ( isWritable() ) { // create/trunc if writable
+ if ( flags() & IO_Truncate )
+ oflags |= (O_CREAT | O_TRUNC);
+ else
+ oflags |= O_CREAT;
+ }
+#if defined(HAS_TEXT_FILEMODE)
+ if ( isTranslated() )
+ oflags |= OPEN_TEXT;
+ else
+ oflags |= OPEN_BINARY;
+#endif
+#if defined(HAS_ASYNC_FILEMODE)
+ if ( isAsynchronous() )
+ oflags |= OPEN_ASYNC;
+#endif
+ fd = qt_open( QFile::encodeName(fn), oflags, 0666 );
+
+ if ( fd != -1 ) { // open successful
+ ::fstat( fd, &st ); // get the stat for later usage
+ } else {
+ ok = FALSE;
+ }
+ } else { // buffered file I/O
+ QCString perm;
+ char perm2[4];
+ bool try_create = FALSE;
+ if ( flags() & IO_Append ) { // append to end of file?
+ setFlags( flags() | IO_WriteOnly ); // append implies write
+ perm = isReadable() ? "a+" : "a";
+ } else {
+ if ( isReadWrite() ) {
+ if ( flags() & IO_Truncate ) {
+ perm = "w+";
+ } else {
+ perm = "r+";
+ try_create = TRUE; // try to create if not exists
+ }
+ } else if ( isReadable() ) {
+ perm = "r";
+ } else if ( isWritable() ) {
+ perm = "w";
+ }
+ }
+ qstrcpy( perm2, perm );
+#if defined(HAS_TEXT_FILEMODE)
+ if ( isTranslated() )
+ strcat( perm2, "t" );
+ else
+ strcat( perm2, "b" );
+#endif
+ for (;;) { // At most twice
+
+ fh = fopen( QFile::encodeName(fn), perm2 );
+
+ if ( !fh && try_create ) {
+ perm2[0] = 'w'; // try "w+" instead of "r+"
+ try_create = FALSE;
+ } else {
+ break;
+ }
+ }
+ if ( fh ) {
+ ::fstat( fileno(fh), &st ); // get the stat for later usage
+ } else {
+ ok = FALSE;
+ }
+ }
+ if ( ok ) {
+ setState( IO_Open );
+ // on successful open the file stat was got; now test what type
+ // of file we have
+ if ( (st.st_mode & S_IFMT) != S_IFREG ) {
+ // non-seekable
+ setType( IO_Sequential );
+ length = INT_MAX;
+ ioIndex = 0;
+ } else {
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ length = st.st_size > UINT_MAX ? UINT_MAX : (Offset)st.st_size;
+#else
+ length = (Offset)st.st_size;
+#endif
+ ioIndex = (flags() & IO_Append) == 0 ? 0 : length;
+ if ( !(flags()&IO_Truncate) && length == 0 && isReadable() ) {
+ // try if you can read from it (if you can, it's a sequential
+ // device; e.g. a file in the /proc filesystem)
+ int c = getch();
+ if ( c != -1 ) {
+ ungetch(c);
+ setType( IO_Sequential );
+ length = INT_MAX;
+ ioIndex = 0;
+ }
+ resetStatus();
+ }
+ }
+ } else {
+ init();
+ if ( errno == EMFILE ) // no more file handles/descrs
+ setStatus( IO_ResourceError );
+ else
+ setStatus( IO_OpenError );
+ setErrorStringErrno( errno );
+ }
+ return ok;
+}
+
+/*!
+ \overload
+ Opens a file in the mode \a m using an existing file handle \a f.
+ Returns TRUE if successful, otherwise FALSE.
+
+ Example:
+ \code
+ #include <stdio.h>
+
+ void printError( const char* msg )
+ {
+ QFile f;
+ f.open( IO_WriteOnly, stderr );
+ f.writeBlock( msg, qstrlen(msg) ); // write to stderr
+ f.close();
+ }
+ \endcode
+
+ When a QFile is opened using this function, close() does not actually
+ close the file, only flushes it.
+
+ \warning If \a f is \c stdin, \c stdout, \c stderr, you may not
+ be able to seek. See QIODevice::isSequentialAccess() for more
+ information.
+
+ \sa close()
+*/
+
+bool QFile::open( int m, FILE *f )
+{
+ if ( isOpen() ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QFile::open: File already open" );
+#endif
+ return FALSE;
+ }
+ init();
+ setMode( m &~IO_Raw );
+ setState( IO_Open );
+ fh = f;
+ ext_f = TRUE;
+ struct stat st;
+ ::fstat( fileno(fh), &st );
+#if defined(QT_LARGEFILE_SUPPORT)
+#if !defined(QT_ABI_QT4)
+ off_t tmp = ftello( fh );
+ ioIndex = tmp > UINT_MAX ? UINT_MAX : (Offset)tmp;
+#else
+ ioIndex = (Offset)ftello( fh );
+#endif
+#else
+ ioIndex = (Offset)ftell( fh );
+#endif
+ if ( (st.st_mode & S_IFMT) != S_IFREG || f == stdin ) { //stdin is non seekable
+ // non-seekable
+ setType( IO_Sequential );
+ length = INT_MAX;
+ ioIndex = 0;
+ } else {
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ length = st.st_size > UINT_MAX ? UINT_MAX : (Offset)st.st_size;
+#else
+ length = (Offset)st.st_size;
+#endif
+ if ( !(flags()&IO_Truncate) && length == 0 && isReadable() ) {
+ // try if you can read from it (if you can, it's a sequential
+ // device; e.g. a file in the /proc filesystem)
+ int c = getch();
+ if ( c != -1 ) {
+ ungetch(c);
+ setType( IO_Sequential );
+ length = INT_MAX;
+ ioIndex = 0;
+ }
+ resetStatus();
+ }
+ }
+ return TRUE;
+}
+
+/*!
+ \overload
+ Opens a file in the mode \a m using an existing file descriptor \a f.
+ Returns TRUE if successful, otherwise FALSE.
+
+ When a QFile is opened using this function, close() does not actually
+ close the file.
+
+ The QFile that is opened using this function, is automatically set to be in
+ raw mode; this means that the file input/output functions are slow. If you
+ run into performance issues, you should try to use one of the other open
+ functions.
+
+ \warning If \a f is one of 0 (stdin), 1 (stdout) or 2 (stderr), you may not
+ be able to seek. size() is set to \c INT_MAX (in limits.h).
+
+ \sa close()
+*/
+
+
+bool QFile::open( int m, int f )
+{
+ if ( isOpen() ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QFile::open: File already open" );
+#endif
+ return FALSE;
+ }
+ init();
+ setMode( m |IO_Raw );
+ setState( IO_Open );
+ fd = f;
+ ext_f = TRUE;
+ struct stat st;
+ ::fstat( fd, &st );
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ off_t tmp = ::lseek(fd, 0, SEEK_CUR);
+ ioIndex = tmp > UINT_MAX ? UINT_MAX : (Offset)tmp;
+#else
+ ioIndex = (Offset)::lseek(fd, 0, SEEK_CUR);
+#endif
+ if ( (st.st_mode & S_IFMT) != S_IFREG || f == 0 ) { // stdin is not seekable...
+ // non-seekable
+ setType( IO_Sequential );
+ length = INT_MAX;
+ ioIndex = 0;
+ } else {
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ length = st.st_size > UINT_MAX ? UINT_MAX : (Offset)st.st_size;
+#else
+ length = (Offset)st.st_size;
+#endif
+ if ( length == 0 && isReadable() ) {
+ // try if you can read from it (if you can, it's a sequential
+ // device; e.g. a file in the /proc filesystem)
+ int c = getch();
+ if ( c != -1 ) {
+ ungetch(c);
+ setType( IO_Sequential );
+ length = INT_MAX;
+ ioIndex = 0;
+ }
+ resetStatus();
+ }
+ }
+ return TRUE;
+}
+
+/*!
+ Returns the file size.
+ \sa at()
+*/
+
+QIODevice::Offset QFile::size() const
+{
+ struct stat st;
+ int ret = 0;
+ if ( isOpen() ) {
+ ret = ::fstat( fh ? fileno(fh) : fd, &st );
+ } else {
+ ret = ::stat( QFile::encodeName(fn), &st );
+ }
+ if ( ret == -1 )
+ return 0;
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ return (uint)st.st_size > UINT_MAX ? UINT_MAX : (Offset)st.st_size;
+#else
+ return st.st_size;
+#endif
+}
+
+
+/*!
+ \overload
+
+ Sets the file index to \a pos. Returns TRUE if successful;
+ otherwise returns FALSE.
+
+ Example:
+ \code
+ QFile f( "data.bin" );
+ f.open( IO_ReadOnly ); // index set to 0
+ f.at( 100 ); // set index to 100
+ f.at( f.at()+50 ); // set index to 150
+ f.at( f.size()-80 ); // set index to 80 before EOF
+ f.close();
+ \endcode
+
+ Use \c at() without arguments to retrieve the file offset.
+
+ \warning The result is undefined if the file was open()'ed using
+ the \c IO_Append specifier.
+
+ \sa size(), open()
+*/
+
+bool QFile::at( Offset pos )
+{
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "QFile::at: File is not open" );
+#endif
+ return FALSE;
+ }
+ if ( isSequentialAccess() )
+ return FALSE;
+ bool ok;
+ if ( isRaw() ) {
+ off_t l = ::lseek( fd, pos, SEEK_SET );
+ ok = ( l != -1 );
+ pos = (Offset)l;
+ } else { // buffered file
+#if defined(QT_LARGEFILE_SUPPORT)
+ ok = ( ::fseeko(fh, pos, SEEK_SET) == 0 );
+#else
+ ok = ( ::fseek(fh, pos, SEEK_SET) == 0 );
+#endif
+ }
+ if ( ok )
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ ioIndex = pos > UINT_MAX ? UINT_MAX : (Offset)pos;
+#else
+ ioIndex = (Offset)pos;
+#endif
+#if defined(QT_CHECK_RANGE)
+ else
+#if defined(QT_ABI_QT4)
+ qWarning( "QFile::at: Cannot set file position %lld", pos );
+#else
+ qWarning( "QFile::at: Cannot set file position %lu", pos );
+#endif
+#endif
+ return ok;
+}
+
+/*!
+ \reimp
+
+ \warning We have experienced problems with some C libraries when a buffered
+ file is opened for both reading and writing. If a read operation takes place
+ immediately after a write operation, the read buffer contains garbage data.
+ Worse, the same garbage is written to the file. Calling flush() before
+ readBlock() solved this problem.
+*/
+
+Q_LONG QFile::readBlock( char *p, Q_ULONG len )
+{
+ if ( !len ) // nothing to do
+ return 0;
+
+#if defined(QT_CHECK_NULL)
+ if ( !p )
+ qWarning( "QFile::readBlock: Null pointer error" );
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "QFile::readBlock: File not open" );
+ return -1;
+ }
+ if ( !isReadable() ) {
+ qWarning( "QFile::readBlock: Read operation not permitted" );
+ return -1;
+ }
+#endif
+ Q_ULONG nread = 0; // number of bytes read
+ if ( !ungetchBuffer.isEmpty() ) {
+ // need to add these to the returned string.
+ uint l = ungetchBuffer.length();
+ while( nread < l ) {
+ *p = ungetchBuffer.at( l - nread - 1 );
+ p++;
+ nread++;
+ }
+ ungetchBuffer.truncate( l - nread );
+ }
+
+ if ( nread < len ) {
+ if ( isRaw() ) { // raw file
+ nread += ::read( fd, p, len-nread );
+ if ( len && nread <= 0 ) {
+ nread = 0;
+ setStatus(IO_ReadError);
+ setErrorStringErrno( errno );
+ }
+ } else { // buffered file
+ nread += fread( p, 1, len-nread, fh );
+ if ( (uint)nread != len ) {
+ if ( ferror( fh ) || nread==0 ) {
+ setStatus(IO_ReadError);
+ setErrorString( qt_fileerr_read );
+ }
+ }
+ }
+ }
+ if ( !isSequentialAccess() )
+ ioIndex += nread;
+ return nread;
+}
+
+
+/*! \reimp
+
+ Writes \a len bytes from \a p to the file and returns the number of
+ bytes actually written.
+
+ Returns -1 if a serious error occurred.
+
+ \warning When working with buffered files, data may not be written
+ to the file at once. Call flush() to make sure the data is really
+ written.
+
+ \sa readBlock()
+*/
+
+Q_LONG QFile::writeBlock( const char *p, Q_ULONG len )
+{
+ if ( !len ) // nothing to do
+ return 0;
+
+#if defined(QT_CHECK_NULL)
+ if ( p == 0 && len != 0 )
+ qWarning( "QFile::writeBlock: Null pointer error" );
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) { // file not open
+ qWarning( "QFile::writeBlock: File not open" );
+ return -1;
+ }
+ if ( !isWritable() ) { // writing not permitted
+ qWarning( "QFile::writeBlock: Write operation not permitted" );
+ return -1;
+ }
+#endif
+ Q_ULONG nwritten; // number of bytes written
+ if ( isRaw() ) // raw file
+ nwritten = ::write( fd, (void *)p, len );
+ else // buffered file
+ nwritten = fwrite( p, 1, len, fh );
+ if ( nwritten != len ) { // write error
+ if ( errno == ENOSPC ) // disk is full
+ setStatus( IO_ResourceError );
+ else
+ setStatus( IO_WriteError );
+ setErrorStringErrno( errno );
+ if ( !isSequentialAccess() ) {
+ if ( isRaw() ) { // recalc file position
+#if defined(QT_LARGEFILE_SUPPORT) && !defined(QT_ABI_QT4)
+ off_t tmp = ::lseek( fd, 0, SEEK_CUR );
+ ioIndex = tmp > UINT_MAX ? UINT_MAX : (Offset)tmp;
+#else
+ ioIndex = (Offset)::lseek( fd, 0, SEEK_CUR );
+#endif
+ } else {
+#if defined(QT_LARGEFILE_SUPPORT)
+#if !defined(QT_ABI_QT4)
+ off_t tmp = (Offset)::fseeko( fh, 0, SEEK_CUR );
+ ioIndex = tmp > UINT_MAX ? UINT_MAX : (Offset)tmp;
+#else
+ ioIndex = (Offset)::fseeko( fh, 0, SEEK_CUR );
+#endif
+#else
+ ioIndex = (Offset)::fseek( fh, 0, SEEK_CUR );
+#endif
+ }
+ }
+ } else {
+ if ( !isSequentialAccess() )
+ ioIndex += nwritten;
+ }
+ if ( ioIndex > length ) // update file length
+ length = ioIndex;
+ return nwritten;
+}
+
+/*!
+ Returns the file handle of the file.
+
+ This is a small positive integer, suitable for use with C library
+ functions such as fdopen() and fcntl(). On systems that use file
+ descriptors for sockets (ie. Unix systems, but not Windows) the handle
+ can be used with QSocketNotifier as well.
+
+ If the file is not open or there is an error, handle() returns -1.
+
+ \sa QSocketNotifier
+*/
+
+int QFile::handle() const
+{
+ if ( !isOpen() )
+ return -1;
+ else if ( fh )
+ return fileno( fh );
+ else
+ return fd;
+}
+
+/*!
+ Closes an open file.
+
+ The file is not closed if it was opened with an existing file handle.
+ If the existing file handle is a \c FILE*, the file is flushed.
+ If the existing file handle is an \c int file descriptor, nothing
+ is done to the file.
+
+ Some "write-behind" filesystems may report an unspecified error on
+ closing the file. These errors only indicate that something may
+ have gone wrong since the previous open(). In such a case status()
+ reports IO_UnspecifiedError after close(), otherwise IO_Ok.
+
+ \sa open(), flush()
+*/
+
+
+void QFile::close()
+{
+ bool ok = FALSE;
+ if ( isOpen() ) { // file is not open
+ if ( fh ) { // buffered file
+ if ( ext_f )
+ ok = fflush( fh ) != -1; // flush instead of closing
+ else
+ ok = fclose( fh ) != -1;
+ } else { // raw file
+ if ( ext_f )
+ ok = TRUE; // cannot close
+ else
+ ok = ::close( fd ) != -1;
+ }
+ init(); // restore internal state
+ }
+ if (!ok) {
+ setStatus( IO_UnspecifiedError );
+ setErrorStringErrno( errno );
+ }
+}