Fix for UBSan build
[platform/upstream/doxygen.git] / qtools / qdir_win32.cpp
1 /******************************************************************************
2  *
3  * 
4  *
5  * Copyright (C) 1997-2001 by Dimitri van Heesch.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation under the terms of the GNU General Public License is hereby 
9  * granted. No representations are made about the suitability of this software 
10  * for any purpose. It is provided "as is" without express or implied warranty.
11  * See the GNU General Public License for more details.
12  *
13  * Documents produced by Doxygen are derivative works derived from the
14  * input used in their production; they are not affected by this license.
15  *
16  * Based on qdir_unix.cpp 
17  *
18  * Copyright (C) 1992-2000 Trolltech AS.
19  */
20
21
22 #include "qglobal.h"
23
24 #include "qdir.h"
25 #ifndef QT_NO_DIR
26
27
28 #include "qfileinfo.h"
29 #include "qfiledefs_p.h"
30 #include "qregexp.h"
31 #include "qstringlist.h"
32 #include <stdlib.h>
33 #include <ctype.h>
34 #if defined(_OS_WIN32_)
35 #if defined(_CC_BOOL_DEF_)
36 #undef  bool
37 #include <windows.h>
38 #define bool int
39 #else
40 #include <windows.h>
41 #endif
42 #endif
43 #if defined(_OS_OS2EMX_)
44 extern Q_UINT32 DosQueryCurrentDisk(Q_UINT32*,Q_UINT32*);
45 #define NO_ERROR 0
46 #endif
47
48 extern QStringList qt_makeFilterList( const QString &filter );
49
50 extern int qt_cmp_si_sortSpec;
51
52 #if defined(Q_C_CALLBACKS)
53 extern "C" {
54 #endif
55
56 extern int qt_cmp_si( const void *, const void * );
57
58 #if defined(Q_C_CALLBACKS)
59 }
60 #endif
61
62 static QString p_getenv( QString name )
63 {
64   DWORD len = GetEnvironmentVariableW( ( LPCWSTR ) qt_winTchar ( name, TRUE ), NULL, 0 );
65   if ( len == 0 )
66     return QString::null;
67   /* ansi: we allocate too much memory, but this shouldn't be the problem here ... */
68   LPWSTR buf = (LPWSTR)new WCHAR[ len ];
69   len = GetEnvironmentVariableW ( ( LPCWSTR ) qt_winTchar ( name, TRUE ), buf, len );
70   if ( len == 0 )
71   {
72     delete[] buf;
73     return QString::null;
74   }
75   QString ret = qt_winQString ( buf );
76   delete[] buf;
77   return ret;
78 }
79
80
81 void QDir::slashify( QString& n )
82 {
83   for ( int i=0; i<(int)n.length(); i++ ) 
84   {
85      if ( n[i] == '\\' )
86           n[i] = '/';
87   }
88 }
89
90 QString QDir::homeDirPath()
91 {
92   QString d = p_getenv ( "HOME" );
93   if ( d.isNull () ) {
94     d = p_getenv ( "USERPROFILE" );
95     if ( d.isNull () ) {
96       QString homeDrive = p_getenv ( "HOMEDRIVE" );
97       QString homePath = p_getenv ( "HOMEPATH" );
98       if ( !homeDrive.isNull () && !homePath.isNull () ) {
99         d = homeDrive + homePath;
100       } else {
101         d = rootDirPath ();
102       }
103     }
104   }
105   slashify( d );
106   return d;
107 }
108
109 QString QDir::canonicalPath() const
110 {
111     QString r;
112
113     char cur[PATH_MAX];
114     char tmp[PATH_MAX];
115     GETCWD( cur, PATH_MAX );
116     if ( CHDIR(QFile::encodeName(dPath)) >= 0 ) {
117         GETCWD( tmp, PATH_MAX );
118         r = QFile::decodeName(tmp);
119     }
120     CHDIR( cur );
121
122     slashify( r );
123     return r;
124 }
125
126 bool QDir::mkdir( const QString &dirName, bool acceptAbsPath ) const
127 {
128 #if defined(__CYGWIN32_)
129     return MKDIR( QFile::encodeName(filePath(dirName,acceptAbsPath)), 0777 ) == 0;
130 #else
131     return _wmkdir( ( LPCWSTR ) filePath( dirName, acceptAbsPath ).ucs2() ) == 0;
132 #endif
133 }
134
135 bool QDir::rmdir( const QString &dirName, bool acceptAbsPath ) const
136 {
137 #if defined(__CYGWIN32_)
138     return RMDIR( QFile::encodeName(filePath(dirName,acceptAbsPath)) ) == 0;
139 #else
140     return _wrmdir( ( LPCWSTR ) filePath( dirName, acceptAbsPath ).ucs2() ) == 0;
141 #endif
142 }
143
144 bool QDir::isReadable() const
145 {
146     QString path = dPath;
147     if ( ( path[ 0 ] == '\\' ) || ( path[ 0 ] == '/' ) )
148         path = rootDirPath() + path;
149 #if defined(__CYGWIN32_)
150     return ACCESS( QFile::encodeName(dPath), R_OK ) == 0;
151 #else
152     return ( _waccess( (wchar_t*) path.ucs2(), R_OK ) == 0 );
153 #endif
154 }
155
156 bool QDir::isRoot() const
157 {
158     QString path = dPath;
159     slashify( path );
160     return path == rootDirPath ();
161 }
162
163 bool QDir::rename( const QString &name, const QString &newName,
164                    bool acceptAbsPaths  )
165 {
166     if ( name.isEmpty() || newName.isEmpty() ) {
167 #if defined(CHECK_NULL)
168         qWarning( "QDir::rename: Empty or null file name(s)" );
169 #endif
170         return FALSE;
171     }
172     QString fn1 = filePath( name, acceptAbsPaths );
173     QString fn2 = filePath( newName, acceptAbsPaths );
174 #if defined(__CYGWIN32_)
175     return ::rename( QFile::encodeName(fn1),
176                      QFile::encodeName(fn2) ) == 0;
177 #else
178     return MoveFileW( ( LPCWSTR ) fn1.ucs2(), ( LPCWSTR ) fn2.ucs2() ) != 0;    
179 #endif
180 }
181
182 bool QDir::setCurrent( const QString &path )
183 {
184 #if defined(__CYGWIN32_)
185     int r;
186     r = CHDIR( QFile::encodeName(path) );
187     return r >= 0;
188 #else
189     if ( !QDir( path ).exists() )
190         return false;
191     return ( SetCurrentDirectoryW( ( LPCWSTR ) path.ucs2() ) >= 0 );
192 #endif
193 }
194
195 QString QDir::currentDirPath()
196 {
197     QString result;
198
199 #if defined(__CYGWIN32_)
200
201     STATBUF st;
202     if ( STAT( ".", &st ) == 0 ) {
203         char currentName[PATH_MAX];
204         if ( GETCWD( currentName, PATH_MAX ) != 0 )
205             result = QFile::decodeName(currentName);
206 #if defined(DEBUG)
207         if ( result.isNull() )
208             qWarning( "QDir::currentDirPath: getcwd() failed" );
209 #endif
210     } else {
211 #if defined(DEBUG)
212         qWarning( "QDir::currentDirPath: stat(\".\") failed" );
213 #endif
214     }
215
216 #else
217
218     DWORD size = 0;
219     WCHAR currentName[ PATH_MAX ];
220     size = ::GetCurrentDirectoryW( PATH_MAX, currentName );
221     if ( size != 0 ) {
222       if ( size > PATH_MAX ) {
223         WCHAR * newCurrentName = new WCHAR[ size ];
224         if ( ::GetCurrentDirectoryW( PATH_MAX, newCurrentName ) != 0 )
225           result = QString::fromUcs2( ( ushort* ) newCurrentName );
226         delete [] newCurrentName;
227       } else {
228         result = QString::fromUcs2( ( ushort* ) currentName );
229       }
230     }
231
232     if ( result.length() >= 2 && result[ 1 ] == ':' )
233           result[ 0 ] = result.at( 0 ).upper(); // Force uppercase drive letters.
234 #endif
235     slashify( result );
236     return result;
237 }
238
239 QString QDir::rootDirPath()
240 {
241   QString d = p_getenv ( "SystemDrive" );
242   if ( d.isNull () )
243     d = QString::fromLatin1( "c:" );  // not "c:\\" !
244   slashify ( d );
245   return d;
246 }
247
248 bool QDir::isRelativePath( const QString &path )
249 {
250   if ( path.isEmpty() )
251     return TRUE;
252   int p = 0;
253   if ( path[ 0 ].isLetter() && path[ 1 ] == ':' )
254     p = 2; // we have checked the first 2.
255   return ( ( path[ p ] != '/' ) && ( path[ p ] != '\\' ) );
256 }
257
258 #undef  IS_SUBDIR
259 #undef  IS_RDONLY
260 #undef  IS_ARCH
261 #undef  IS_HIDDEN
262 #undef  IS_SYSTEM
263 #undef  FF_GETFIRST
264 #undef  FF_GETNEXT
265 #undef  FF_ERROR
266
267 #if defined(_OS_WIN32_)
268 #define IS_SUBDIR   FILE_ATTRIBUTE_DIRECTORY
269 #define IS_RDONLY   FILE_ATTRIBUTE_READONLY
270 #define IS_ARCH     FILE_ATTRIBUTE_ARCHIVE
271 #define IS_HIDDEN   FILE_ATTRIBUTE_HIDDEN
272 #define IS_SYSTEM   FILE_ATTRIBUTE_SYSTEM
273 #define FF_GETFIRST FindFirstFile
274 #define FF_GETNEXT  FindNextFile
275 #define FF_ERROR    INVALID_HANDLE_VALUE
276 #else
277 #define IS_SUBDIR   _A_SUBDIR
278 #define IS_RDONLY   _A_RDONLY
279 #define IS_ARCH     _A_ARCH
280 #define IS_HIDDEN   _A_HIDDEN
281 #define IS_SYSTEM   _A_SYSTEM
282 #define FF_GETFIRST _findfirst
283 #define FF_GETNEXT  _findnext
284 #define FF_ERROR    -1
285 #endif
286
287
288 bool QDir::readDirEntries( const QString &nameFilter,
289                            int filterSpec, int sortSpec )
290 {
291     int i;
292     if ( !fList ) {
293         fList  = new QStringList;
294         CHECK_PTR( fList );
295         fiList = new QFileInfoList;
296         CHECK_PTR( fiList );
297         fiList->setAutoDelete( TRUE );
298     } else {
299         fList->clear();
300         fiList->clear();
301     }
302
303     QStringList filters = qt_makeFilterList( nameFilter );
304
305     bool doDirs     = (filterSpec & Dirs)       != 0;
306     bool doFiles    = (filterSpec & Files)      != 0;
307     bool noSymLinks = (filterSpec & NoSymLinks) != 0;
308     bool doReadable = (filterSpec & Readable)   != 0;
309     bool doWritable = (filterSpec & Writable)   != 0;
310     bool doExecable = (filterSpec & Executable) != 0;
311     bool doHidden   = (filterSpec & Hidden)     != 0;
312     // show hidden files if the user asks explicitly for e.g. .*
313     if ( !doHidden && !nameFilter.isEmpty() && nameFilter[0] == '.' )
314          doHidden = TRUE;
315     bool doModified = (filterSpec & Modified)   != 0;
316     bool doSystem   = (filterSpec & System)     != 0;
317
318     QRegExp   wc( nameFilter.data(), FALSE, TRUE );    // wild card, case insensitive
319     bool      first = TRUE;
320     QString   p = dPath.copy();
321     int       plen = p.length();
322 #if defined(_OS_WIN32_)
323     HANDLE    ff;
324     WIN32_FIND_DATAW finfo;
325 #else
326     long      ff;
327     _finddata_t finfo;
328 #endif
329     QFileInfo fi;
330     if ( plen == 0 )
331     {
332 #if defined(CHECK_NULL)
333       warning( "QDir::readDirEntries: No directory name specified" );
334 #endif
335       return FALSE;
336     }
337     if ( p.at(plen-1) != '/' && p.at(plen-1) != '\\' )
338           p += '/';
339     p += "*.*";
340
341 #if defined(__CYGWIN32_)
342     ff = FF_GETFIRST( p.data(), &finfo );
343 #else
344     ff = FindFirstFileW ( ( LPCWSTR ) p.ucs2(), &finfo );
345 #endif
346     if ( ff == FF_ERROR ) 
347     {
348 #if defined(DEBUG)
349       warning( "QDir::readDirEntries: Cannot read the directory: %s",
350                            (const char *)dPath.utf8() );
351 #endif
352     return FALSE;
353     }
354     
355     while ( TRUE ) 
356     {
357         if ( first )
358             first = FALSE;
359         else 
360         {
361 #if defined(__CYGWIN32_)
362             if ( FF_GETNEXT(ff,&finfo) == -1 )
363                 break;
364 #else
365             //if ( !FF_GETNEXT(ff,&finfo) )
366             //  break;
367             if (!FindNextFileW(ff, &finfo ))
368                 break;
369 #endif
370         }
371 #if defined(__CYGWIN32_)
372         int  attrib = finfo.attrib;
373 #else
374         int  attrib = finfo.dwFileAttributes;
375 #endif
376         bool isDir      = (attrib & IS_SUBDIR) != 0;
377         bool isFile     = !isDir;
378         bool isSymLink  = FALSE;
379         bool isReadable = TRUE;
380         bool isWritable = (attrib & IS_RDONLY) == 0;
381         bool isExecable = FALSE;
382         bool isModified = (attrib & IS_ARCH)   != 0;
383         bool isHidden   = (attrib & IS_HIDDEN) != 0;
384         bool isSystem   = (attrib & IS_SYSTEM) != 0;
385
386 #if defined(__CYGWIN32_)
387         const char *fname = finfo.name;
388 #else
389         //const char *fname = finfo.cFileName;
390         QString fname = QString::fromUcs2( ( const unsigned short* ) finfo.cFileName);
391 #endif
392         if ( wc.match(fname.utf8()) == -1 && !(allDirs && isDir) )
393             continue;
394
395         QString name = fname;
396         if ( doExecable ) 
397         {
398             QString ext = name.right(4).lower();
399             if ( ext == ".exe" || ext == ".com" || ext == ".bat" ||
400                  ext == ".pif" || ext == ".cmd" )
401                 isExecable = TRUE;
402         }
403
404         if  ( (doDirs && isDir) || (doFiles && isFile) ) 
405         {
406             if ( noSymLinks && isSymLink )
407                 continue;
408             if ( (filterSpec & RWEMask) != 0 )
409                 if ( (doReadable && !isReadable) ||
410                      (doWritable && !isWritable) ||
411                      (doExecable && !isExecable) )
412                     continue;
413             if ( doModified && !isModified )
414                 continue;
415             if ( !doHidden && isHidden )
416                 continue;
417             if ( !doSystem && isSystem )
418                 continue;
419             fi.setFile( *this, name );
420             fiList->append( new QFileInfo( fi ) );
421         }
422     }
423 #if defined(__CYGWIN32_)
424     _findclose( ff );
425 #else
426     FindClose( ff );
427 #endif
428
429     // Sort...
430     QDirSortItem* si= new QDirSortItem[fiList->count()];
431     QFileInfo* itm;
432     i=0;
433     for (itm = fiList->first(); itm; itm = fiList->next())
434         si[i++].item = itm;
435     qt_cmp_si_sortSpec = sortSpec;
436     qsort( si, i, sizeof(si[0]), qt_cmp_si );
437     // put them back in the list
438     fiList->setAutoDelete( FALSE );
439     fiList->clear();
440     int j;
441     for ( j=0; j<i; j++ ) {
442         fiList->append( si[j].item );
443         fList->append( si[j].item->fileName() );
444     }
445     delete [] si;
446     fiList->setAutoDelete( TRUE );
447
448     if ( filterSpec == (FilterSpec)filtS && sortSpec == (SortSpec)sortS &&
449          nameFilter == nameFilt )
450         dirty = FALSE;
451     else
452         dirty = TRUE;
453     return TRUE;
454 }
455
456 const QFileInfoList * QDir::drives()
457 {
458     // at most one instance of QFileInfoList is leaked, and this variable
459     // points to that list
460     static QFileInfoList * knownMemoryLeak = 0;
461
462     if ( !knownMemoryLeak ) {
463         knownMemoryLeak = new QFileInfoList;
464
465 #if defined(_OS_WIN32_)
466         Q_UINT32 driveBits = (Q_UINT32) GetLogicalDrives() & 0x3ffffff;
467 #elif defined(_OS_OS2EMX_)
468         Q_UINT32 driveBits, cur;
469         if (DosQueryCurrentDisk(&cur,&driveBits) != NO_ERROR)
470             exit(1);
471         driveBits &= 0x3ffffff;
472 #endif
473         char driveName[4];
474         qstrcpy( driveName, "a:/" );
475         while( driveBits ) {
476             if ( driveBits & 1 )
477                 knownMemoryLeak->append( new QFileInfo( driveName ) );
478             driveName[0]++;
479             driveBits = driveBits >> 1;
480         }
481     }
482
483     return knownMemoryLeak;
484 }
485 #endif //QT_NO_DIR