// Copyright (c) 1997-1999 Microsoft Corporation // // File System services. // // 8-14-97 (sburns) #ifndef FILESYS_HPP_INCLUDED #define FILESYS_HPP_INCLUDED // CODEWORK: remove exceptions in favor of an HRESULT orientation. namespace Burnslib { namespace FS { // Facilitates walking a directory tree. // CODEWORK: add methods to extract current file data beyond just the // name. class Iterator { public: enum { INCLUDE_FILES = 0x0001, INCLUDE_FOLDERS = 0x0002, INCLUDE_DOT_PATHS = 0x0004, // CODEWORK: // EXPAND_SUBDIRS = 0x0008, RETURN_FULL_PATHS = 0x0010 }; // Constructs a new instance of an Iterator. // // startingPathSpec - fully-qualified path specification of the // files/directories to be iterated upon. A wildcard // specification is allowed at the end of the path. E.g. // "C:\dir\*.txt" // // (Without a wildcard expression of some kind, the iteration set will // be the single file or folder that matches startingPathSpec. This set // may be further reduced to the empty set if the optionMask eliminates // the single match.) // // optionMask - Options, OR'ed together. explicit Iterator( const String& startingPathSpec, unsigned optionMask = INCLUDE_FILES | INCLUDE_FOLDERS | INCLUDE_DOT_PATHS /* | EXPAND_SUBDIRS */ | RETURN_FULL_PATHS); ~Iterator(); // Restores the iterator to the state that it had upon // construction. void Reset(); // Retrieve the name of the file at the current iterator position. If // the iterator was constructed with the RETURN_FULL_PATHS Option, then // the returned string is a fully-qualified path, instead of a path // relative to the starting path the Iterator was constructed with. // Returns S_OK on success, S_FALSE if the iteration is empty (there are // no files), or an error code. // // If AtEnd() is true, then the empty string is returned. // // result - receives the file path at the current position of the // iterator, or the empty string if the iteration set is empty, or // an error occurred. HRESULT GetCurrent(String& result); // Move the current position to the next file, according to the // iterator filtering options. May cause the iterator to become // invalid, which can be tested with AtEnd(). Returns S_OK on success // S_FALSE when the iteration is complete, or an error code. HRESULT Increment(); private: WIN32_FIND_DATA* findData; HANDLE findHandle; bool finished; unsigned options; String parentFolder; String startSearchSpec; void Finish(); bool IsNotStarted(); String ReturnPath(); bool ShouldSkipCurrent(); HRESULT Start(); HRESULT SkipFilteredPaths(); // copying not implemented in the interest of simplicity (could be done // in theory) Iterator(const Iterator&); const Iterator& operator=(const Iterator&); }; // Simple file-to-file copy. // // sourceFile - Fully-qalified path of the file to be copied. This // path must reference an existing file. // // destinationFile - Fully-qualified path of the file to be // created. This file is always overwritten, if it exists. All // intermediate subdirectories required are created. // // progressCallback - Callback object to receive progress // notifications. The param argument to the callback's Execute // method will be an instance of CopyCallbackParam. The method // should return !0 to abort the copy. struct CopyCallbackParam { String sourceFile; String destinationFile; int percentCopied; }; HRESULT CopyFile( const String& sourceFile, const String& destinationFile, Callback* progressCallback); // Creates a directory, including all intermediate subdirectories, // as necessary. Returns S_FALSE if the path already exists. // // path - Fully-qualified path to be created. It must not // already exist. HRESULT CreateFolder(const String& path); // Opens a file for shared read/write access with normal attributes, // creating it if it does not already exist. // // path - Fully-qualified path of file to open. If path doesn't // exist, it is created, including intermediate subdirectories. // // result - receives the resulting file handle, on success. On falure, // this is set to INVALID_HANDLE_VALUE // // REVIEWED-2002/02/26-sburns we require full absolute or unc file paths, // else we assert and return E_INVALIDARG. HRESULT CreateFile( const String& path, HANDLE& result, DWORD desiredAccess, DWORD shareMode = 0, // we don't require all callers to specify an SD in case the call // just to open the file. DWORD creationDisposition = OPEN_ALWAYS, DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL); // Deletes a file. Returns S_OK on success, or an error code on failure. // // path - Fully-qualified path of file to delete. HRESULT DeleteFile(const String& path); // Splits a fully-qualified path into its constituent parts. // // drive - receives the volume portion (in the form "X:") // // parentFolderPath - receives the path of the folder containing the leaf // file or folder. // // leafName - receives the base name of the last file or folder on the // path. // // extension - receives the extension, of the last file or folder on the // path, including the dot (".ext") void SplitPath( const String& fullpath, String& drive, String& parentFolderPath, String& leafName, String& extension); // Appends an unqualified relative path (i.e. system32\cys.exe) to the // the base path supplied and returns the full path. A '\' will be added // between the parts if needed. If the base path is not normalized the // result will not be normalized either. // // base - fully-qualified path which will be appended to // // additional - unqualified relative path which will be appended String AppendPath( const String& base, const String& additional); // Returns the leaf portion of a fully-qualified path, including the // extension. The path may refer to either a file or a folder. For // example, "x:\foo\bar.ext" returns "bar.ext" // // fullpath - fully-qualified filename. String GetPathLeafElement(const String& fullpath); // Removes the last component of a fully-qualified file name or folder // name. Includes trailing path separator only if the parent folder is the // root folder on a volume. // // e.g. "x:\foo" returns "x:\", but "x:\foo\bar" returns "x:\foo" (not // "x:\foo\") // // fullpath - fully-qualified filename. String GetParentFolder(const String& fullpath); // Returns the available space, in bytes, to the current user of the // calling thread (i.e. takes into account user quotas) // // path - Fully-qualified path of file/directory for which attributes will // be retrieved. This need not be the root directory of the volume in // question. // // result - receives the result, the available space in bytes. Set to // 0 on error. HRESULT GetAvailableSpace(const String& path, ULONGLONG& result); // Returns the root folder path of the given full path, e.g. for // "C:\foo\bar" returns "C:\" // // fullpath - Fully-qualified path of file/directory String GetRootFolder(const String& fullpath); // Reports the current position of the file read/write pointer. Returns // S_OK on success, or an error code on failure. // // handle - valid handle to an opened file // // result - receives the file position, set to 0 on error HRESULT GetFilePosition(HANDLE handle, LONGLONG& result); // Returns the total size, in bytes, of the file opened on the provided // handle. // // handle - valid handle to an opened file // // result - receives the file size, or 0 on error. HRESULT GetFileSize(HANDLE handle, LONGLONG& result); // Returns the type of the file system of the volume on which the path // refers. Non-existant paths are considered to have the FAT file system. // // path - fully-qualified path, which contains the drive letter of the // volume // CODEWORK: how does this behave in the presence of mount points? For // example, what if a FAT volume is mounted as a subdirectory of an // NTFS volume? enum FSType { FAT, CDFS, NTFS4, NTFS5 }; FSType GetFileSystemType(const String& path); enum PathSyntax { SYNTAX_ABSOLUTE_DRIVE, // d:\foo\bar SYNTAX_ABSOLUTE_DRIVE_WILDCARD, // d:\foo\*.* SYNTAX_ABSOLUTE_NO_DRIVE, // \foo\bar SYNTAX_ABSOLUTE_NO_DRIVE_WILDCARD, // \foo\*.* SYNTAX_RELATIVE_DRIVE, // d:foo\bar SYNTAX_RELATIVE_DRIVE_WILDCARD, // d:foo\*.* SYNTAX_RELATIVE_NO_DRIVE, // foo\bar SYNTAX_RELATIVE_NO_DRIVE_WILDCARD, // foo\*.* SYNTAX_UNC, // \\machine\share SYNTAX_UNC_WILDCARD, // \\machine\share\*.* SYNTAX_UNRECOGNIZED }; // Parses the supplied string an attempts to validate its syntax. The // string need not refer to an existing file or directory. // // str - the string to be analysed. PathSyntax GetPathSyntax(const String& str); // Populates the given DriveList with elements representing the valid // drive letters on the local machine. Each element is a string of the // form "X:" where X is a drive letter. // // BackInsertableContainer - any type that supports the construction of // a back_insert_iterator on itself, and has a value type that can be // constructed from an PWSTR. // // bii - a reference to a back_insert_iterator of the // BackInsertableContainer template parameter. The simplest way to make // one of these is to use the back_inserter helper function. // // Example: // // StringList container; // hr = FS::GetValidDrives(std::back_inserter(container)); // // StringVector container2; // hr = FS::GetValidDrives(std::back_inserter(container2)); template HRESULT GetValidDrives(std::back_insert_iterator& bii) { HRESULT hr = S_OK; WCHAR* buf = 0; do { // first call determines the size of the buffer we need. DWORD bufchars = 0; hr = Win::GetLogicalDriveStrings(0, 0, bufchars); BREAK_ON_FAILED_HRESULT(hr); // add 1 for extra-safe null terminator ++bufchars; buf = new WCHAR[bufchars]; ::ZeroMemory(buf, bufchars * sizeof WCHAR); // second call actually retrieves the strings DWORD unused = 0; hr = Win::GetLogicalDriveStrings(bufchars - 1, buf, unused); BREAK_ON_FAILED_HRESULT(hr); // walk thru buf and chop it into substrings. for ( // ISSUE-2002/02/22-sburns consider using safe version of wcschr, if // there is one WCHAR* sub = wcschr(buf, 0), *buf2 = buf; sub && buf2 && buf2[0]; buf2 = sub + 1, sub = wcschr(buf2, 0)) { *bii++ = buf2; } } while (0); delete[] buf; return hr; } // Wrapper of the Win32 API of the same name // // path - Fully-qualified path. This path need not exist. HRESULT GetVolumePathName(const String& path, String&); // Returns true if the path refers to an empty or non-existent directory. // // path - Fully-qualified path bool IsFolderEmpty(const String& path); // Returns true if parent is the name of a parent directory of the given // child directory, false if not. A parent directory is defined as one // that appears closer to the root than a child on the same branch. A // parent may be any superior directory (e.g. grandparent, // great-grandparent), not just the immediate superior. // // parent - valid, fully-qualified path of supposed parent directory. Need // not exist. // // child - valid, fully-qualified path of child directory. Need not exist. bool IsParentFolder(const String& parent, const String& child); // Checks the validity, but not the existence of, the specified // file or directory. the path must be absolute and include the // drive specifier. // // path - Fully-qualified path. bool IsValidPath(const String& path); // Moves or renames an existing file or directory. // // sourcePath - Fully-qualified path of the file/directory to be // moved/renamed. This file or directory must exist. If the path refers to // a directory, the directory and all of its children are moved. // // destinationPath - Fully-qualified path of the destination // file/directory. This path need not be on the same volume as the // sourcePath, but if it is not, the move will result in a recursive copy // of the sourcePath. // // replaceExisting - If the destinationPath refers to an existing // file/directory, and this parameter is true, the destinationPath is // overwritten. Otherwise, if the destinationPath exists, an error is // returned. HRESULT MoveFile( const String& sourcePath, const String& destinationPath); // CODEWORK: // bool replaceExisting = false); // "Normalize" a path by parsing any relative path components (like . and // ..), and return the resulting path. If there are no relative // components, or if an error occurred, return the same string as the // input. // // The result is not guaranteed to be syntactically correct, or to refer to // and existing file. For that, use IsValidPath() and PathExists() // // Example: // L"c:\\.\\.\\..\\.\\temp\\.\\foo\\bar\\..\\baz" results in // L"c:\\temp\\foo\\baz" // // abnormalPath - path to parse. String NormalizePath(const String& abnormalPath); // Returns true if the specified file or directory exists. // // path - Fully-qualified path. bool PathExists(const String& path); // Returns true if the specified file exists, i.e. the path exists and // it refers to a file (as opposed to a folder) bool FileExists(const String& filePath); // Reads bytes from the current file pointer of the handle as Unicode // text (2 bytes/character). // // handle - valid, open file handle, with read/write pointer positioned // to the first byte of the first character to be read. // // charactersToRead - number of characters to read. -1 to read all // characters up to the end of the file or the first null character // encountered. If this number would cause a read past the end of the // file, or past a null character, the read will stop at the end of the // file or null character. In the case of a null character, the file // read/write pointer will be positioned at the byte following the null // character. // // text - the characters read. A truncated read operation can be detected // by comparing the length of this string to the charactersToRead // parameter. HRESULT Read(HANDLE handle, int charactersToRead, String& text); // Reads bytes from the current file pointer of the handle as ANSI // text. // // handle - valid, open file handle, with read/write pointer positioned // to the first byte of the first character to be read. // // bytesToRead - count of the number of bytes (*NOT* characters) to read // // text - the bytes read. HRESULT Read(HANDLE handle, int bytesToRead, AnsiString& text); // Positions the file read/write pointer. // // handle - Valid, open file handle. // // position - new position of the pointer, from the beginning of the // file. HRESULT Seek(HANDLE handle, LONGLONG position); // Moves the file read/write pointer to the end of the file. // // handle - Valid, open file handle. HRESULT SeekToEnd(HANDLE handle); // Writes the supplied string as Unicode text to the file. // // handle - Valid, open file handle. // // text - the text to be written HRESULT Write(HANDLE handle, const String& text); // appends a crlf HRESULT WriteLine(HANDLE handle, const String& text); // Writes the supplied buffer to the file. // // handle - Valid, open file handle. // // buf - buffer to write. This is an instance of basic_string HRESULT Write(HANDLE handle, const AnsiString& buf); // appends a crlf HRESULT WriteLine(HANDLE handle, const AnsiString& text); } } #endif // FILESYS_HPP_INCLUDED