source: trunk/src/tools/qdir_pm.cpp

Last change on this file was 187, checked in by dmik, 17 years ago

Tools: QDir::canonicalPath(): Use realpath()/DosQueryPathInfo() instead of non-threadsafe chdir() approach.

  • Property svn:keywords set to Id
File size: 13.4 KB
Line 
1/****************************************************************************
2** $Id: qdir_pm.cpp 187 2008-11-09 21:47:20Z dmik $
3**
4** Implementation of QDir class
5**
6** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the tools module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qplatformdefs.h"
39
40#include "qdir.h"
41#include "qdir_p.h"
42#include "qnamespace.h"
43#include "qfileinfo.h"
44#include "qfiledefs_p.h"
45#include "qregexp.h"
46#include "qstringlist.h"
47
48#ifdef QT_THREAD_SUPPORT
49# include <private/qmutexpool_p.h>
50#endif // QT_THREAD_SUPPORT
51
52#include "qt_os2.h"
53
54#include <stdlib.h>
55
56void QDir::slashify( QString& n )
57{
58 if ( n.isNull() )
59 return;
60 for ( int i=0; i<(int)n.length(); i++ ) {
61 if ( n[i] == '\\' )
62 n[i] = '/';
63 }
64}
65
66QString QDir::homeDirPath()
67{
68 QString d;
69 d = QFile::decodeName( getenv("HOME") );
70 if ( d.isEmpty() || !QFile::exists( d ) ) {
71 d = QFile::decodeName( getenv("HOMEDRIVE") ) + QFile::decodeName( getenv("HOMEPATH") );
72 if ( d.isEmpty() || !QFile::exists( d ) )
73 d = rootDirPath();
74 }
75 slashify( d );
76 return d;
77}
78
79/*!
80 Returns the canonical path, i.e. a path without symbolic links or
81 redundant "." or ".." elements.
82
83 On systems that do not have symbolic links this function will
84 always return the same string that absPath() returns. If the
85 canonical path does not exist (normally due to dangling symbolic
86 links) canonicalPath() returns QString::null.
87
88 \sa path(), absPath(), exists(), cleanDirPath(), dirName(),
89 absFilePath(), QString::isNull()
90*/
91
92QString QDir::canonicalPath() const
93{
94 QString r;
95
96 char tmp[PATH_MAX];
97
98#if defined (__INNOTEK_LIBC__)
99 if ( realpath( QFile::encodeName( dPath ), tmp ) != NULL )
100 r = QFile::decodeName( tmp );
101#else
102 if ( DosQueryPathInfo( QFile::encodeName( dPath ),
103 FIL_QUERYFULLNAME, tmp, sizeof(tmp) ) == 0 )
104 r = QFile::decodeName( tmp );
105#endif
106
107 slashify( r );
108 return r;
109}
110
111/*!
112 Creates a directory.
113
114 If \a acceptAbsPath is TRUE a path starting with a separator ('/')
115 will create the absolute directory; if \a acceptAbsPath is FALSE
116 any number of separators at the beginning of \a dirName will be
117 removed.
118
119 Returns TRUE if successful; otherwise returns FALSE.
120
121 \sa rmdir()
122*/
123
124bool QDir::mkdir( const QString &dirName, bool acceptAbsPath ) const
125{
126 return QT_MKDIR( QFile::encodeName( filePath( dirName, acceptAbsPath ) ) ) == 0;
127}
128
129/*!
130 Removes a directory.
131
132 If \a acceptAbsPath is TRUE a path starting with a separator ('/')
133 will remove the absolute directory; if \a acceptAbsPath is FALSE
134 any number of separators at the beginning of \a dirName will be
135 removed.
136
137 The directory must be empty for rmdir() to succeed.
138
139 Returns TRUE if successful; otherwise returns FALSE.
140
141 \sa mkdir()
142*/
143
144bool QDir::rmdir( const QString &dirName, bool acceptAbsPath ) const
145{
146 return QT_RMDIR( QFile::encodeName( filePath( dirName, acceptAbsPath ) ) ) == 0;
147}
148
149
150/*!
151 Returns TRUE if the directory is readable \e and we can open files
152 by name; otherwise returns FALSE.
153
154 \warning A FALSE value from this function is not a guarantee that
155 files in the directory are not accessible.
156
157 \sa QFileInfo::isReadable()
158*/
159
160bool QDir::isReadable() const
161{
162 return QT_ACCESS( QFile::encodeName( dPath ), R_OK ) == 0;
163}
164
165/*!
166 Returns TRUE if the directory is the root directory; otherwise
167 returns FALSE.
168
169 Note: If the directory is a symbolic link to the root directory
170 this function returns FALSE. If you want to test for this use
171 canonicalPath(), e.g.
172 \code
173 QDir d( "/tmp/root_link" );
174 d = d.canonicalPath();
175 if ( d.isRoot() )
176 qWarning( "It is a root link" );
177 \endcode
178
179 \sa root(), rootDirPath()
180*/
181
182bool QDir::isRoot() const
183{
184 return dPath == "/" || dPath == "//" ||
185 (dPath[0].isLetter() && dPath.mid(1,dPath.length()) == ":/");
186}
187
188/*!
189 Renames a file or directory.
190
191 If \a acceptAbsPaths is TRUE a path starting with a separator
192 ('/') will rename the file with the absolute path; if \a
193 acceptAbsPaths is FALSE any number of separators at the beginning
194 of the names will be removed.
195
196 Returns TRUE if successful; otherwise returns FALSE.
197
198 On most file systems, rename() fails only if \a oldName does not
199 exist or if \a newName and \a oldName are not on the same
200 partition. On Windows, rename() will fail if \a newName already
201 exists. However, there are also other reasons why rename() can
202 fail. For example, on at least one file system rename() fails if
203 \a newName points to an open file.
204*/
205
206bool QDir::rename( const QString &oldName, const QString &newName,
207 bool acceptAbsPaths )
208{
209 if ( oldName.isEmpty() || newName.isEmpty() ) {
210#if defined(QT_CHECK_NULL)
211 qWarning( "QDir::rename: Empty or null file name" );
212#endif
213 return FALSE;
214 }
215 QString fn1 = filePath( oldName, acceptAbsPaths );
216 QString fn2 = filePath( newName, acceptAbsPaths );
217 return ::rename( QFile::encodeName( fn1 ), QFile::encodeName( fn2 ) ) == 0;
218}
219/*!
220 Sets the application's current working directory to \a path.
221 Returns TRUE if the directory was successfully changed; otherwise
222 returns FALSE.
223*/
224
225
226bool QDir::setCurrent( const QString &path )
227{
228 return QT_CHDIR( QFile::encodeName( path ) ) == 0;
229}
230
231/*!
232 Returns the absolute path of the application's current directory.
233
234 \sa current()
235*/
236
237QString QDir::currentDirPath()
238{
239 QString result;
240
241 char currentName[PATH_MAX];
242 if ( QT_GETCWD(currentName,PATH_MAX) != 0 ) {
243 result = QFile::decodeName( currentName );
244 }
245 slashify( result );
246 return result;
247}
248
249/*!
250 Returns the absolute path for the root directory.
251
252 For UNIX operating systems this returns "/". For Windows file
253 systems this normally returns "c:/". For OS/2 this returns "x:/"
254 where \c x is the system boot drive letter.
255
256 \sa root() drives()
257*/
258
259
260QString QDir::rootDirPath()
261{
262 ULONG bootDrive = 0;
263 DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, (PVOID) &bootDrive, sizeof(bootDrive) );
264 QString d = QChar( (char)(bootDrive + 'A' - 1) );
265 d += ":/";
266 return d;
267}
268
269/*!
270 Returns TRUE if \a path is relative; returns FALSE if it is
271 absolute.
272
273 \sa isRelative()
274*/
275
276bool QDir::isRelativePath( const QString &path )
277{
278 return !(path[0] == '/' || path[0] == '\\' ||
279 (path[0].isLetter() && path[1] == ':')); // drive, e.g. a: or network drive
280}
281
282/*!
283 \internal
284 Reads directory entries.
285*/
286
287bool QDir::readDirEntries( const QString &nameFilter,
288 int filterSpec, int sortSpec )
289{
290 int i;
291
292 QValueList<QRegExp> filters = qt_makeFilterList( nameFilter );
293
294 bool doDirs = (filterSpec & Dirs) != 0;
295 bool doFiles = (filterSpec & Files) != 0;
296 bool noSymLinks = (filterSpec & NoSymLinks) != 0;
297 bool doReadable = (filterSpec & Readable) != 0;
298 bool doWritable = (filterSpec & Writable) != 0;
299 bool doExecable = (filterSpec & Executable) != 0;
300
301 // FilterSpec::Drives is mentioned in the docs but never used in Qt/Win32.
302 // So do we for compatibility reasons.
303
304 bool first = TRUE;
305 QString p = dPath.copy();
306 int plen = p.length();
307 HDIR ff = (HDIR) HDIR_CREATE;
308 FILEFINDBUF3 finfo = {0};
309 ULONG fcount = 1;
310 APIRET rc = 0;
311 ULONG attribute = FILE_READONLY | FILE_ARCHIVED | FILE_DIRECTORY;
312 QFileInfo fi;
313
314 if (filterSpec & Modified) attribute |= MUST_HAVE_ARCHIVED;
315 if (filterSpec & Hidden) attribute |= FILE_HIDDEN;
316 if (filterSpec & System) attribute |= FILE_SYSTEM;
317
318 if ( plen == 0 ) {
319#if defined(QT_CHECK_NULL)
320 qWarning( "QDir::readDirEntries: No directory name specified" );
321#endif
322 return FALSE;
323 }
324 if ( p.at(plen-1) != '/' && p.at(plen-1) != '\\' )
325 p += '/';
326 p += QString::fromLatin1("*.*");
327
328 rc = DosFindFirst( QFile::encodeName( p ), &ff, attribute,
329 &finfo, sizeof(finfo), &fcount, FIL_STANDARD );
330
331 if ( !fList ) {
332 fList = new QStringList;
333 Q_CHECK_PTR( fList );
334 } else {
335 fList->clear();
336 }
337
338 if ( rc != 0 ) {
339 // if it is a floppy disk drive, it might just not have a file on it
340 if ( plen > 1 && p[1] == ':' &&
341 ( p[0]=='A' || p[0]=='a' || p[0]=='B' || p[0]=='b' ) ) {
342 if ( !fiList ) {
343 fiList = new QFileInfoList;
344 Q_CHECK_PTR( fiList );
345 fiList->setAutoDelete( TRUE );
346 } else {
347 fiList->clear();
348 }
349 return TRUE;
350 }
351#if defined(QT_CHECK_RANGE)
352 qWarning( "QDir::readDirEntries: Cannot read the directory: %s (UTF8)",
353 dPath.utf8().data() );
354#endif
355 return FALSE;
356 }
357
358 if ( !fiList ) {
359 fiList = new QFileInfoList;
360 Q_CHECK_PTR( fiList );
361 fiList->setAutoDelete( TRUE );
362 } else {
363 fiList->clear();
364 }
365
366 for ( ;; ) {
367 if ( first )
368 first = FALSE;
369 else {
370 if ( DosFindNext( ff, &finfo, sizeof(finfo), &fcount) != 0 )
371 break;
372 }
373 int attrib = finfo.attrFile;
374 bool isDir = (attrib & FILE_DIRECTORY) != 0;
375 bool isFile = !isDir;
376 bool isSymLink = FALSE;
377 bool isReadable = TRUE;
378 bool isWritable = (attrib & FILE_READONLY) == 0;
379 bool isExecable = FALSE;
380
381 QString fname;
382 fname = QFile::decodeName( (const char*)finfo.achName );
383
384 if ( !qt_matchFilterList(filters, fname) && !(allDirs && isDir) )
385 continue;
386
387 if ( (doDirs && isDir) || (doFiles && isFile) ) {
388 QString name = fname;
389 slashify(name);
390 if ( doExecable && isFile ) {
391 QString ext = name.right(4).lower();
392 if ( ext == ".exe" || ext == ".com" || ext == ".bat" ||
393 ext == ".cmd" )
394 isExecable = TRUE;
395 }
396
397 if ( noSymLinks && isSymLink )
398 continue;
399 if ( (filterSpec & RWEMask) != 0 )
400 if ( (doReadable && !isReadable) ||
401 (doWritable && !isWritable) ||
402 (doExecable && !isExecable) )
403 continue;
404 fi.setFile( *this, name );
405 fiList->append( new QFileInfo( fi ) );
406 }
407 }
408 DosFindClose( ff );
409
410 // Sort...
411 QDirSortItem* si= new QDirSortItem[fiList->count()];
412 QFileInfo* itm;
413 i=0;
414 for (itm = fiList->first(); itm; itm = fiList->next())
415 si[i++].item = itm;
416 qt_cmp_si_sortSpec = sortSpec;
417 qsort( si, i, sizeof(si[0]), qt_cmp_si );
418 // put them back in the list
419 fiList->setAutoDelete( FALSE );
420 fiList->clear();
421 int j;
422 for ( j=0; j<i; j++ ) {
423 fiList->append( si[j].item );
424 fList->append( si[j].item->fileName() );
425 }
426 delete [] si;
427 fiList->setAutoDelete( TRUE );
428
429 if ( filterSpec == (FilterSpec)filtS && sortSpec == (SortSpec)sortS &&
430 nameFilter == nameFilt )
431 dirty = FALSE;
432 else
433 dirty = TRUE;
434 return TRUE;
435}
436
437
438/*!
439 Returns a list of the root directories on this system. On Windows
440 this returns a number of QFileInfo objects containing "C:/", "D:/"
441 etc. On other operating systems, it returns a list containing just
442 one root directory (e.g. "/").
443
444 The returned pointer is owned by Qt. Callers should \e not delete
445 or modify it.
446*/
447
448const QFileInfoList * QDir::drives()
449{
450 // at most one instance of QFileInfoList is leaked, and this variable
451 // points to that list
452 static QFileInfoList * knownMemoryLeak = 0;
453
454#ifdef QT_THREAD_SUPPORT
455 QMutexLocker locker( qt_global_mutexpool ?
456 qt_global_mutexpool->get( &knownMemoryLeak ) : 0 );
457#endif // QT_THREAD_SUPPORT
458
459 if ( !knownMemoryLeak ) {
460 knownMemoryLeak = new QFileInfoList;
461 knownMemoryLeak->setAutoDelete( TRUE );
462 }
463
464 if ( !knownMemoryLeak->count() ) {
465 ULONG driveBits, dummy;
466 DosQueryCurrentDisk( &dummy, &driveBits );
467 driveBits &= 0x3ffffff;
468
469 char driveName[4];
470 qstrcpy( driveName, "A:/" );
471
472 while( driveBits ) {
473 if ( driveBits & 1 )
474 knownMemoryLeak->append( new QFileInfo( QString::fromLatin1(driveName).upper() ) );
475 driveName[0]++;
476 driveBits = driveBits >> 1;
477 }
478 }
479
480 return knownMemoryLeak;
481}
Note: See TracBrowser for help on using the repository browser.