convert line ends
[canaan.git] / prj / tech / libsrc / cpptools / filepath.cpp
blob527bf8116ff9d6e724fda74edf91d530326820a3
1 ///////////////////////////////////////////////////////////////////////////////
2 // $Source: x:/prj/tech/libsrc/cpptools/RCS/filepath.cpp $
3 // $Author: JUSTIN $
4 // $Date: 1998/07/09 09:46:29 $
5 // $Revision: 1.7 $
6 //
7 // (c) Copyright 1993-1996 Tom Leonard. All Rights Reserved. Unlimited license granted to Looking Glass Technologies Inc.
8 //
10 #if defined(_WIN32)
11 #include <windows.h>
12 #endif
14 #include <lg.h>
15 #include <assert.h>
16 #include <filepath.h>
17 #include <fnamutil.h>
19 #include <ctype.h>
21 #include <direct.h>
22 #include <dos.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <io.h>
27 #include <filespec.h>
28 #include <splitpat.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
33 #ifndef DEBUG_REDUCE
34 #define DEBUG_REDUCE 0
35 #else
36 #pragma message "DEBUG_REDUCE"
37 #endif
39 #ifndef DEBUG_CASE
40 #define DEBUG_CASE 0
41 #else
42 #pragma message "DEBUG_CASE"
43 #endif
46 #ifdef __WATCOMC__
47 #define FA_NORMAL _A_NORMAL
48 #define FA_RDONLY _A_RDONLY
49 #define FA_HIDDEN _A_HIDDEN
50 #define FA_SYSTEM _A_SYSTEM
51 #define FA_LABEL _A_VOLID
52 #define FA_DIREC _A_SUBDIR
53 #define FA_ARCH _A_ARCH
54 #endif
56 #ifdef __WATCOMC__
57 static int _chdrive(int iDrive)
59 unsigned nDummy;
60 _dos_setdrive(iDrive, &nDummy);
61 return !(_getdrive() == iDrive);
63 #endif
65 static char BASED_CODE pcszWildAll[] = "*.*";
68 // Default constructor
70 cFilePath::cFilePath()
76 // Copy constructor
78 cFilePath::cFilePath(const cFilePath &filePath)
80 *this = filePath;
85 // Construct a file path from a file specification
87 cFilePath::cFilePath(const cFileSpec &fileSpec)
89 FromFileSpec(fileSpec.GetName());
94 // Construct a file path from a text string
96 cFilePath::cFilePath(const char *pPath)
98 FromText(pPath);
103 // Set the path from another path
105 cFilePath &cFilePath::operator=(const cFilePath& filePath)
107 m_path = filePath.m_path;
108 return *this;
113 // Set the path from a file specification
115 cFilePath &cFilePath::operator=(const cFileSpec& fileSpec)
117 FromFileSpec(fileSpec.GetName());
118 return *this;
123 // Set the path from a file specification string
125 void cFilePath::FromFileSpec(const char *pFileSpec)
127 assert(pFileSpec);
129 cPathSplitter PathSplitter(pFileSpec);
130 cStr &s = m_path;
132 unsigned nLengthName = PathSplitter.GetDriveLen() + PathSplitter.GetDirectoryLen();
133 if (nLengthName)
135 char * p = s.GetBuffer(nLengthName);
136 strncpy(p, PathSplitter.GetDrive(), PathSplitter.GetDriveLen());
137 strncpy(p + PathSplitter.GetDriveLen(), PathSplitter.GetDirectory(), PathSplitter.GetDirectoryLen());
138 p[nLengthName] = '\0';
139 s.ReleaseBuffer(nLengthName);
141 else
142 s.Empty();
144 // If the path isn't empty...
145 if (!m_path.IsEmpty())
147 char c = m_path[m_path.GetLength()-1];
149 // If there is no trailing slash...
150 if (c != '\\')
151 m_path += '\\';
155 #ifdef _MSC_VER
156 #define S_ISDIR(m) (m & _S_IFDIR)
157 #endif
160 // Check for existence
162 BOOL cFilePath::PathExists() const
164 struct _stat StatBuf;
165 cStr StrFullPath;
166 if (IsEmpty() || !GetFullPath(StrFullPath))
167 return FALSE;
169 const unsigned nLenFull = StrFullPath.GetLength();
171 if (nLenFull < 2)
172 return FALSE;
174 char * const pName = StrFullPath.Detach();
176 AssertMsg(pName[nLenFull-1] == '\\' || pName[nLenFull-1] == '/', "Bad file path detected");
177 pName[nLenFull - 1] = '\0'; // get rid of '\\'
179 // Previously used access(), but that doesn't distinguish between
180 // a file and a directory:
181 // int result = access(pName, 0);
182 int result = _stat(pName, &StatBuf);
183 if (result == 0) {
184 // Okay, it exists -- is it actually a directory?
185 if (S_ISDIR(StatBuf.st_mode)) {
186 // Yep, it is
187 } else {
188 // Nope -- unset result
189 result = -1;
193 free(pName);
195 return result == 0;
200 // Check for existence
202 BOOL cFilePath::CreatePath() const
204 cStr StrFullPath;
205 if (IsEmpty() || !GetFullPath(StrFullPath))
206 return FALSE;
208 const unsigned nLenFull = StrFullPath.GetLength();
210 if (nLenFull < 2)
211 return FALSE;
213 char * const pName = StrFullPath.Detach();
215 AssertMsg(pName[nLenFull-1] == '\\' || pName[nLenFull-1] == '/', "Bad file path detected");
216 pName[nLenFull - 1] = '\0'; // get rid of '\\'
218 int result = mkdir(pName);
220 free(pName);
222 return result != -1;
227 // Set the path from a full path string
229 void cFilePath::FromText(const char *pPath)
231 assert(pPath);
232 m_path = pPath;
234 // If the path isn't empty...
235 if (!m_path.IsEmpty())
237 char c = m_path[m_path.GetLength()-1];
239 // If there is no trailing slash...
240 if (c != '\\')
241 m_path += '\\';
249 // Return the path name as a text string
251 void cFilePath::AsText(cStr& str) const
253 str = m_path;
258 // Change directory to this path
260 BOOL cFilePath::SetCurrentPath() const
262 BEGIN_DEBUG_MSG("cFilePath::SetCurrentPath()")
264 // Get the file and extension
265 cPathSplitter PathSplitter(m_path);
267 cStr drive;
268 cStr dir;
269 PathSplitter.GetSplit(&drive, &dir);
271 DebugMsg2("Split into '%s' and '%s'",drive.BufIn(),dir.BufIn());
273 // Need to strip trailing '\' from dir
274 if (dir.GetLength() > 1)
275 dir.Remove(dir.GetLength()-1, 1);
277 DebugMsg2("Stripped down to '%s' and '%s'",drive.BufIn(),dir.BufIn());
279 if (drive.GetLength())
281 int driveNum = toupper(drive[0]) - 'A'+1;
283 if (_chdrive(driveNum) != 0)
284 return FALSE;
287 if (chdir(dir) != 0)
288 return FALSE;
290 return TRUE;
292 END_DEBUG;
297 // Add a relative path to the current path
299 BOOL cFilePath::AddRelativePath(const cFilePath &relPath)
301 // Should already be trailed by \...
302 AssertMsg(m_path[m_path.GetLength()-1]=='\\', "Invalid file path");
304 if (relPath.IsEmpty())
305 return TRUE;
307 if (!relPath.IsRelativePath())
308 return FALSE;
310 m_path += relPath.m_path;
312 return ReducePathDots();
317 // Make this path a full path
319 BOOL cFilePath::MakeFullPath()
321 cStr FullPath;
322 if (!GetFullPath(FullPath))
323 return FALSE;
325 m_path = FullPath;
326 return TRUE;
331 // Save the path to the stream
333 BOOL cFilePath::ToStream(cOStore &OStore) const
335 return m_path.ToStream(OStore);
340 // Load the path from the stream
342 BOOL cFilePath::FromStream(cIStore &IStore)
344 return m_path.FromStream(IStore);
349 // See if two paths are logically equal
351 BOOL cFilePath::operator==(const cFilePath &FilePath) const
353 cStr Path1;
354 if (!GetFullPath(Path1))
355 return FALSE;
357 cStr Path2;
358 if (!FilePath.GetFullPath(Path2))
359 return FALSE;
361 return Path1 == Path2;
365 // Filespec Iteration
368 #if defined(_WIN32)
369 #define IsFoundDir(fd) ((fd).dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
370 #endif
372 BOOL sFindContext::DoFindFirst(tFindState WhatToFind, const cStr & StrSpec,
373 cStr & StrResult)
375 BEGIN_DEBUG_MSG2("sfc DoFindFirst(%d,%s,)",WhatToFind,StrSpec.BufIn());
376 #if defined(_WIN32)
377 DebugMsg("...Win32");
378 if (PlatformFindContext.hContext)
379 if (!FindClose(PlatformFindContext.hContext))
380 return FALSE;
382 WIN32_FIND_DATA FindData;
384 if ((PlatformFindContext.hContext = FindFirstFile(StrSpec, &FindData)) != INVALID_HANDLE_VALUE)
386 DebugMsg2("FindFirstFile(%s) %c",(char*)FindData.cFileName, IsFoundDir(FindData)?'d':'f');
387 while ((WhatToFind == kFindingFiles) ? IsFoundDir(FindData) : !IsFoundDir(FindData))
389 if (!FindNextFile(PlatformFindContext.hContext, &FindData))
391 FindClose(PlatformFindContext.hContext);
392 PlatformFindContext.hContext = NULL;
393 Finding = kFindingNone;
394 return FALSE;
396 DebugMsg2("... skipped, trying %s %c",(char*)FindData.cFileName, IsFoundDir(FindData)?'d':'f');
398 Finding = WhatToFind;
399 StrResult = FindData.cFileName;
400 return TRUE;
403 PlatformFindContext.hContext = NULL;
404 Finding = kFindingNone;
406 #else /* Windows or Dos */
407 DebugMsg("...Win/Dos");
409 if (WhatToFind == kFindingFiles)
411 if(0 == _dos_findfirst((char*)((const char *) StrSpec),
412 ~FA_DIREC & ~FA_LABEL,
413 (find_t*)&PlatformFindContext))
415 StrResult = PlatformFindContext.name;
416 Finding = kFindingFiles;
417 return TRUE;
420 else if (WhatToFind == kFindingPaths)
422 // dos_findfirst(...FA_DIREC...) does not exclude non-directories;
423 // it simply prevents the normal exclusion of directories.
424 // (See MS DOS Ref for 21h#4Eh, 21h#11h.)
425 // Thus, must post-filter normals out of FA_DIREC results etc.
426 if(0 == _dos_findfirst((char*)((const char *) StrSpec),
427 FA_DIREC,
428 (find_t*)&PlatformFindContext))
430 // Usually the first will be "." which has FA_DIREC attrib,
431 // but Novell Roots don't have "." even if Novell is supposed to show ".."
432 // in which case the first may be a normal file not a directory.
433 // (Even nonroot Novell dirs may not have "." if user doesn't set SHOWDOTS ?)
434 if (!(PlatformFindContext.attrib & FA_DIREC))
438 if (0 != _dos_findnext((find_t*)&PlatformFindContext))
440 WhatToFind = kFindingNone;
441 return FALSE; // none/no-more found
443 } while (!(PlatformFindContext.attrib & FA_DIREC));
446 StrResult = PlatformFindContext.name;
447 Finding = kFindingPaths;
448 DebugMsg1("... accepting DOS dir '%s'",StrResult.BufIn());
449 return TRUE;
453 #endif
454 return FALSE;
455 END_DEBUG;
458 BOOL sFindContext::DoFindNext(cStr & StrResult)
460 #if defined(_WIN32)
461 if (!PlatformFindContext.hContext)
462 return FALSE;
464 WIN32_FIND_DATA FindData;
466 if (FindNextFile(PlatformFindContext.hContext, &FindData))
468 while ((Finding == kFindingFiles) ? IsFoundDir(FindData) : !IsFoundDir(FindData))
470 if (!FindNextFile(PlatformFindContext.hContext, &FindData))
472 FindClose(PlatformFindContext.hContext);
473 PlatformFindContext.hContext = NULL;
474 Finding = kFindingNone;
475 return FALSE;
478 StrResult = FindData.cFileName;
479 return TRUE;
482 FindClose(PlatformFindContext.hContext);
483 PlatformFindContext.hContext = NULL;
484 Finding = kFindingNone;
485 return FALSE;
487 #else /* Windows or Dos */
489 if (Finding == kFindingFiles)
491 if (0 != _dos_findnext((find_t*)&PlatformFindContext))
493 Finding = kFindingNone;
494 return FALSE; // none/no-more found
497 else if (Finding == kFindingPaths)
501 if (0 != _dos_findnext((find_t*)&PlatformFindContext))
503 Finding = kFindingNone;
504 return FALSE; // none/no-more found
506 } while (!(PlatformFindContext.attrib & FA_DIREC));
508 StrResult = PlatformFindContext.name;
509 return TRUE;
511 #endif
515 // File path portion of find
517 BOOL cFilePath::FindFirst(cFileSpec &fs, sFindContext& FC) const
519 cStr WcString = m_path + pcszWildAll;
520 cStr StrFoundFile;
522 if (FC.DoFindFirst(sFindContext::kFindingFiles, WcString, StrFoundFile))
524 fs.Empty();
525 fs.SetFileName(StrFoundFile);
526 fs.SetFilePath(*this);
527 return TRUE;
530 FC.Finding = sFindContext::kFindingNone;
531 return FALSE;
534 BOOL cFilePath::FindNext(cFileSpec &fs, sFindContext& FC) const
536 if (FC.Finding != sFindContext::kFindingFiles)
538 CriticalMsg("Must FindFirst before FindNext using cFilePath");
539 return FindFirst(fs,FC);
541 else
543 cStr StrFoundFile;
544 if (FC.DoFindNext(StrFoundFile))
546 fs.Empty();
547 fs.SetFileName(StrFoundFile);
548 fs.SetFilePath(*this);
549 return TRUE;
551 FC.Finding = sFindContext::kFindingNone;
552 return FALSE;
557 // Filepath Iteration
559 BOOL cFilePath::FindFirst(cFilePath &fp, sFindContext& FC) const
561 cStr WcString = m_path + pcszWildAll;
562 cStr StrFoundFile;
564 if (FC.DoFindFirst(sFindContext::kFindingPaths, WcString, StrFoundFile))
566 #if defined(WIN32)
567 if(StrFoundFile == "." || StrFoundFile == "..")
569 return FindNext(fp,FC);
572 fp.m_path = m_path;
573 fp.m_path += StrFoundFile;
574 fp.m_path += '\\';
575 #ifdef DEBUG_FILEPATH
576 int iPathLen = m_path.GetLength();
577 AssertMsg(iPathLen==0||m_path[iPathLen-1]=='\\',
578 "Pathname not ending in \\");
579 #endif
580 return TRUE;
581 #endif
583 FC.Finding = sFindContext::kFindingNone;
584 return FALSE;
587 BOOL cFilePath::FindNext(cFilePath &fp, sFindContext& FC) const
589 if (FC.Finding != sFindContext::kFindingPaths)
591 CriticalMsg("Must FindFirst before FindNext using cFilePath");
592 return FindFirst(fp,FC);
595 cStr StrFoundFile;
597 #if defined(WIN32)
600 if (!FC.DoFindNext(StrFoundFile))
602 FC.Finding = sFindContext::kFindingNone;
603 return FALSE; // none/no-more found
606 while( StrFoundFile == "." || StrFoundFile == "..");
608 fp.m_path = m_path;
609 fp.m_path += StrFoundFile;
610 fp.m_path += '\\';
612 #ifdef DEBUG_FILEPATH
613 int iPathLen = m_path.GetLength();
614 AssertMsg(iPathLen==0||m_path[iPathLen-1]=='\\',
615 "Pathname not ending in \\");
616 #endif
618 return TRUE; // Found one.
619 #else
620 return FALSE;
621 #endif
626 // End the file system search
628 void cFilePath::FindDone(sFindContext & fc) const
630 #if defined(_WIN32)
631 if (fc.PlatformFindContext.hContext)
633 FindClose(fc.PlatformFindContext.hContext);
634 fc.PlatformFindContext.hContext = NULL;
635 fc.Finding = sFindContext::kFindingNone;
637 #endif
642 // What's the name of the root?
644 BOOL cFilePath::GetRootDir(const char *pPath, cStr &Str)
646 if (!pPath || !*pPath)
648 Str.Empty();
649 return FALSE;
652 if (::IsRelativePath(pPath))
654 if (::GetFullPath(pPath, Str.BufOut()))
656 Str.BufDone();
657 cPathSplitter PathSplitter(Str);
658 PathSplitter.GetDrive(Str);
659 Str += '\\';
660 return TRUE;
663 else if (*(pPath+1) == ':')
665 Str += *pPath;
666 Str += ":\\";
668 else if (*pPath == '\\' && *(pPath+1) == '\\')
670 const char * pEndDomain = strchr(pPath+2, '\\');
671 unsigned nAppendLen;
672 if (pEndDomain)
673 nAppendLen = pEndDomain - pPath;
674 else
675 nAppendLen = strlen(pPath);
676 Str.Empty();
677 Str.Append(nAppendLen, pPath);
680 return FALSE;
685 // What's the volume called
687 BOOL cFilePath::GetVolumeName(const char * /* pPath */, cStr & /* Str */)
689 return FALSE;
693 // What kind of file system does it use (FAT, HPFS, Mac, NFS, NTFS, etc.)
696 eFileSystemKind cFilePath::GetFileSystemKind() const
698 // @TBD: This is crude:
699 if (GetCaseConventions() & kNameCaseIgnored)
700 return kFATFileSystem;
701 return kDefaultFileSystem;
704 BOOL cFilePath::GetFileSystemName(const char * /* pPath */, cStr & /* Str */)
706 return FALSE;
711 // Get the serial number
713 ulong cFilePath::GetVolumeSerialNumber(const char * /* pPath */)
715 return 0L;
720 // Get the volume case relevance
722 int cFilePath::GetCaseConventions(const char *pPath)
724 #if defined(_WIN32)
725 BEGIN_DEBUG_MSG_EX1(CASE, "int cFilePath::GetCaseConventions(%s)", pPath);
726 DWORD dwFileSysFlags;
727 cStr StrRootDir;
728 GetRootDir(pPath, StrRootDir);
729 DebugMsgEx1(CASE, "Calling ::GetVolumeInformation(%s, ...)", StrRootDir.operator const char *());
730 if (::GetVolumeInformation(StrRootDir, NULL, 0, NULL, NULL, &dwFileSysFlags, NULL, 0))
732 DebugMsgEx1(CASE, "::GetVolumeInformation() returned %lx", dwFileSysFlags);
734 DebugMsgTrueEx(CASE, (dwFileSysFlags & FS_CASE_IS_PRESERVED), "...volume is case retaining");
735 DebugMsgTrueEx(CASE, (dwFileSysFlags & FS_CASE_SENSITIVE), "...volume is case sensitive");
736 DebugMsgTrueEx(CASE, (dwFileSysFlags & FS_UNICODE_STORED_ON_DISK), "...volume stores unicode");
737 DebugMsgTrueEx(CASE, !(dwFileSysFlags & (FS_CASE_IS_PRESERVED | FS_CASE_SENSITIVE)), "...volume is case ignorant");
739 if (dwFileSysFlags)
741 int iReturn = 0;
742 if (dwFileSysFlags & FS_CASE_IS_PRESERVED)
743 iReturn |= kNameCasePreserved;
744 if (dwFileSysFlags & FS_CASE_SENSITIVE)
745 iReturn |= kNameCaseSensitive;
746 if (dwFileSysFlags & FS_UNICODE_STORED_ON_DISK)
747 iReturn |= kUnicodeStored;
748 if (!(dwFileSysFlags & (FS_CASE_IS_PRESERVED | FS_CASE_SENSITIVE)))
749 iReturn |= kNameCaseIgnored;
750 return iReturn;
753 DebugMsgEx(CASE, "GetVolumeInformation() returned FALSE, ...assuming volume is case ignorant");
754 return kNameCaseIgnored;
755 END_DEBUG;
756 #else
757 return kNameCaseIgnored;
758 #endif
763 // Get the maximum length of a component name
765 ulong cFilePath::GetMaxLegalComponentLen(const char * /* pPath */)
767 #if defined(_WIN32)
768 return 255;
769 #else
770 return 8;
771 #endif
776 // Character okay for volume?
778 BOOL cFilePath::IsValidChar(const char * /* pPath */, char /*c*/)
780 return TRUE;
785 // Component name okay for volume?
787 BOOL cFilePath::IsValidComponentName(const char * /* pPath */, const char * /* psz */)
789 return TRUE;
794 // Get full path relative to arg
796 BOOL cFilePath::GetFullPath(cStr &Str, const cFilePath &fp) const
798 cFilePath fpFullPath(fp);
799 if (fpFullPath.AddRelativePath(*this))
800 return fpFullPath.GetFullPath(Str);
801 return GetFullPath(Str);
806 // Make path full relative to arg
808 BOOL cFilePath::MakeFullPath(const cFilePath &fp)
810 if (IsRelativePath())
812 cFilePath fpFullPath(fp);
813 cFilePath fpRelative(*this);
814 if (fpFullPath.AddRelativePath(*this))
815 *this = fpFullPath;
818 return MakeFullPath();