Files
opennurbs/opennurbs_file_utilities.h
Bozo the Builder 3c751dd0ea Sync changes from upstream repository
Co-authored-by: croudyj <croudyj@gmail.com>
Co-authored-by: Dale Lear <dalelear@mcneel.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: Jussi Aaltonen <jussi@mcneel.com>
Co-authored-by: piac <giulio@mcneel.com>
Co-authored-by: Pierre Cuvilliers <pierre@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
Co-authored-by: Will Pearson <will@mcneel.com>
2023-06-27 14:12:47 -07:00

1815 lines
53 KiB
C++

//
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#if !defined(OPENNURBS_FILE_UTILITIES_INC_)
#define OPENNURBS_FILE_UTILITIES_INC_
class ON_CLASS ON_FileSystem
{
private:
ON_FileSystem() = delete;
~ON_FileSystem() = delete;
ON_FileSystem(const ON_FileSystem&) = delete;
ON_FileSystem& operator=(const ON_FileSystem&) = delete;
public:
static bool PathExists(
const char* path
);
static bool PathExists(
const wchar_t* path
);
/*
Returns:
True if path is a directory.
False otherwise.
*/
static bool IsDirectory(
const char* path
);
/*
Returns:
True if path is a directory.
False otherwise.
*/
static bool IsDirectory(
const wchar_t* path
);
/*
Returns:
True if path is a directory where files can be written.
False otherwise.
*/
static bool IsDirectoryWithWriteAccess(
const char* path
);
/*
Returns:
True if path is a directory where files can be written.
False otherwise.
*/
static bool IsDirectoryWithWriteAccess(
const wchar_t* path
);
/*
Returns:
True if path is a file.
False otherwise.
*/
static bool IsFile(
const char* path
);
/*
Returns:
True if path is a file.
False otherwise.
*/
static bool IsFile(
const wchar_t* path
);
/*
Description
Remove a file
Parameters:
file_path - [in]
name of file to delete
Returns:
True if the file existed and was removed.
*/
static bool RemoveFile(
const char* file_path
);
/*
Description
Remove a file
Parameters:
file_path - [in]
name of file to delete
Returns:
True if the file existed and was removed.
*/
static bool RemoveFile(
const wchar_t* file_path
);
};
class ON_CLASS ON_FileSystemPath
{
private:
ON_FileSystemPath() = delete;
~ON_FileSystemPath() = delete;
ON_FileSystemPath(const ON_FileSystemPath&) = delete;
ON_FileSystemPath& operator=(const ON_FileSystemPath&) = delete;
public:
/*
Platform dependent character used to separate directory names.
On Windows platforms:
ON_FileSystemPath::DirectorySeparator = ON_wString::Backslash.
On UNIX (including modern Apple) platforms:
ON_FileSystemPath::DirectorySeparator = ON_wString::Slash.
*/
static const char DirectorySeparatorAsChar;
static const wchar_t DirectorySeparator;
static const char AlternateDirectorySeparatorAsChar;
static const wchar_t AlternateDirectorySeparator;
static bool IsDirectorySeparator(
char c,
bool bAllowAlternate
);
static bool IsDirectorySeparator(
wchar_t c,
bool bAllowAlternate
);
/*
Description:
Find the locations in a path the specify the drive, directory,
file name and file extension.
Parameters:
path - [in]
path to split
volume - [out] (pass null if you don't need the volume)
If volume is not null and the path parameter begins with a
Windows volume specification, *volume will either be
the Windows volume letter followed by the trailing colon
or a Windows UNC \\<hostname>. Otherwise volume will
be the empty string.
dir - [out] (pass null if you don't need the directory)
If dir is not null and the path parameter contains a
directory specification, then the returned value of *dir
will be the directory specification including the trailing
slash.
file_name_stem - [out] (pass null if you don't need the file name stem)
If file_name_stem is not null and the path parameter contains a
file name specification, then the returned value of *file_name_stem
will be the file name stem.
file_name_ext - [out] (pass null if you don't need the extension)
If file_name_ext is not null and the path parameter contains a
file name extension specification, then the returned value of
*file_name_ext will be the file name extension including the initial
'.' character.
Remarks:
This function will treat a front slash ( / ) and a back slash
( \ ) as directory separators. Because this function parses
file names store in .3dm files and the .3dm file may have been
written on a Windows computer and then read on a another
computer, it looks for a volume specification even when the
operating system is not Windows.
This function will not return an directory that does not
end with a trailing slash.
This function will not return an empty filename and a non-empty
extension.
This function parses the path string according to these rules.
It does not check the actual file system to see if the answer
is correct.
See Also:
on_splitpath
*/
static void SplitPath(
const char* path,
ON_String* volume,
ON_String* dir,
ON_String* file_name_stem,
ON_String* file_name_ext
);
static void SplitPath(
const char* path,
ON_wString* volume,
ON_wString* dir,
ON_wString* file_name_stem,
ON_wString* file_name_ext
);
static void SplitPath(
const wchar_t* path,
ON_wString* volume,
ON_wString* dir,
ON_wString* file_name_stem,
ON_wString* file_name_ext
);
static void SplitPath(
const wchar_t* path,
ON_wString* volume,
ON_wString* dir,
ON_wString* file_name_stem_and_extension
);
static bool FilePathHas3dmExtension(
const wchar_t* file_path,
bool bAllow3dmbakExtension
);
static bool FilePathHas3dmExtension(
const char* file_path,
bool bAllow3dmbakExtension
);
/*
Description:
Determine if the file_name string is a permitted file name.
Valid file names must be non empty, cannot have two periods in a row,
and cannot contain directory separators, tildes, and other
platform specific values.
Parameters:
file_name - [in]
string to test.
bAllPlatforms - [in]
If true, test name for all supported platforms.
Returns:
True if the string can be a file name.
*/
static bool IsValidFileName(
const char* file_name,
bool bAllPlatforms
);
/*
Description:
Determine if the file_name string is a permitted file name.
Valid file names must be non empty, cannot have two periods in a row,
and cannot contain directory separators, tildes, and other
platform specific values.
Parameters:
file_name - [in]
string to test.
bAllPlatforms - [in]
If true, test name for all supported platforms.
Returns:
True if the string can be a file name.
*/
static bool IsValidFileName(
const wchar_t* file_name,
bool bAllPlatforms
);
/*
Parameters:
path - [in]
path to split
Returns:
The volume portion of the path.
*/
static const ON_wString VolumeFromPath(
const wchar_t* path
);
/*
Parameters:
path - [in]
path to split
Returns:
The directory portion of the path.
*/
static const ON_wString DirectoryFromPath(
const wchar_t* path
);
/*
Parameters:
path - [in]
path to split
Returns:
The volume and directory portion of the path.
*/
static const ON_wString VolumeAndDirectoryFromPath(
const wchar_t* path
);
/*
Parameters:
path - [in]
path to split
bIncludeExtension - [in]
Returns:
The file name portion of the path.
*/
static const ON_wString FileNameFromPath(
const wchar_t* path,
bool bIncludeExtension
);
/*
Parameters:
path - [in]
path to split
Returns:
The file name extension portion of the path, including the leading period or "dot".
*/
static const ON_wString FileNameExtensionFromPath(
const wchar_t* path
);
/*
Description:
Condenses // to /
Condenses /./ to /
Condenses /sfsdf/../ to /
Sets all directory separators to directory_separator.
Parameters:
bAllowWindowsUNCHostNameOrDiskLetter - [in]
If bAllowWindowsUNCHostNameOrDiskLetter and the path begins with \\HostName followed by
a directory separator, then the initial \\ is not condensed.
If the path begins with X: followed by a directory separator, where "X" is a single
letter in the range A to Z or a to z, then the path is considered valid.
bDeleteWindowsUNCHostNameOrDiskLetter - [in]
If bAllowWindowsUNCHostNameOrDiskLetter is true and the path begins with a UNC
host name or disk letter followed by a directory separator, then host name or disk letter
is deleted. This is useful when using paths from a Windows platform on a
non-Windows platform.
bExpandUser - [in]
If the path begins with ~ and ON_FileSystemPath::PlatformPath( ON_FileSystemPath::PathId::UserHomeDirectory )
is not empty, then the ~ is replaced with absolute path of user's home directory.
directory_separator - [in]
If 0 == directory_separator, then the first directory separator
is kept when condensing occurs.
ON_wString::FileSystemPathSeparator is a good choice if you want
to use the current runtime's separator.
dirty_path - [in]
path to clean.
Return:
Cleaned path.
*/
static const ON_wString CleanPath(
bool bTrimLeft,
bool bTrimRight,
bool bAllowWindowsUNCHostNameOrDiskLetter,
bool bDeleteWindowsUNCHostNameOrDiskLetter,
bool bExpandUser,
const wchar_t directory_separator,
const wchar_t* dirty_path
);
static const ON_wString CleanPath(
bool bTrimLeft,
bool bTrimRight,
bool bAllowWindowsUNCHostNameOrDiskLetter,
bool bDeleteWindowsUNCHostNameOrDiskLetter,
const wchar_t directory_separator,
const wchar_t* dirty_path
);
/*
Description:
If the path begins with ~ and ON_FileSystemPath::PlatformPath( ON_FileSystemPath::PathId::UserHomeDirectory )
is not empty, then the ~ is replaced with absolute path of user's home directory.
*/
static const ON_wString ExpandUser(
const char* dirty_path
);
static const ON_wString ExpandUser(
const wchar_t* dirty_path
);
/*
Parameters:
path - [in]
path to test
directory_separator - [in]
If 0 == directory_separator, then either ON_wString::FileSystemPathSeparator
or ON_wString::AlternateFileSystemPathSeparator is permitted as a directory
separator.
ON_wString::FileSystemPathSeparator is a good choice if you want
to use the current runtime's separator.
Returns:
True if path begins with ../ or ./
*/
static bool IsRelativePath(
const wchar_t* path,
const wchar_t directory_separator
);
/*
Parameters:
path - [in]
path to test
Returns:
True if path begins with ../ or ..\ or ./ or .\
*/
static bool IsRelativePath(
const wchar_t* path
);
/*
Description:
Condenses // to /
Condenses /./ to /
Condenses /sfsdf/../ to /
Trims left and right white space.
Sets all directory separators to ON_FileSystemPath::DirectorySeparator.
If the Platform is not windows, the UNC host names and volume letters are deleted.
*/
static const ON_wString CleanPath(
const wchar_t* dirty_path
);
/*
Description:
Get a the relative path from base_path to full_path.
Parameters:
full_path - [in]
base_path - [in]
Example
full_path = L"c:/a/b/c/d/somefile.txt";
base_path = L"C:/A/B/X/Y/Z/model.3dm";
ON_wString::GetRelativePath(full_path,base_path) returns
L"../../../c/d/somefile.txt"
Example
full_path = L"c:/a/b/somefile.txt";
base_path = L"C:/A/B/model.3dm";
ON_wString::GetRelativePath(full_path,base_path) returns
L"./somefile.txt"
Remarks:
Path separators on the input can be mixed.
Path separators on the returned relative path are ON_wString::FileSystemPathSeparator
*/
static const ON_wString RelativePath(
const wchar_t* full_path,
bool bFullPathIncludesFileName,
const wchar_t* base_path,
bool bBasePathIncludesFileName
);
static const ON_wString FullPathFromRelativePath(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
const wchar_t* relative_path
);
/*
Returns:
true if the platform file system ignores case.
Remarks:
Windows and default installations of OS X 10.10.3, and default installations of the UNIX
terminal interface in OS X 10.10.3 and later ignore case.
In the case of OX X, a user can override the default setting.
*/
static bool PlatformPathIgnoreCase();
/*
Parameters:
bWithTrailingDirectorySeparator - [in]
true - returned path will have a trailing directory separator.
false - returned path will not have a trailing directory separator.
Returns:
The platform current working directory which should be the directory
where ON::OpenFile("fname","r") would look for a file named "fname".
*/
static const ON_wString CurrentDirectory(
bool bWithTrailingDirectorySeparator
);
/*
Description:
Removes file name from path.
Parameters:
path - [in]
file system path with a file name.
file_name - [out]
If file_name is not nullptr, the removed portion of path
is returned here.
Returns:
path with any file name removed.
Remarks:
This function uses on_wsplitpath() to decide if the path ends with characters that
could be a file name. It does not inspect the file system to see if the file exists.
*/
static const ON_wString RemoveFileName(
const wchar_t* path,
ON_wString* file_name
);
/*
Description:
Removes Windows volume name from path.
Parameters:
path - [in]
file system path
volume_name - [out]
If volume_name is not nullptr, the removed portion of path
is returned here.
Returns:
path with any volume name removed.
Remarks:
This function uses on_wsplitpath() to decide if the path begins with characters that
could be a volume name. It does not inspect the file system to see if the volume exists.
*/
static const ON_wString RemoveVolumeName(
const wchar_t* path,
ON_wString* volume_name
);
/*
Description:
Combine paths into a single valid path name. Remove internal .. and .
directory references. If necessary remove file names.
Parameters:
left_side - [in]
bLeftSideContainsFileName - [in]
true if left_side path ends in a file name and that
file name is removed and discarded.
right_side - [in]
bRightSideContainsFileName - [in]
true if right_side path ends in a file name.
If bAppendTrailingDirectorySeparator is true, that file name is removed
and discarded. If bAppendTrailingDirectorySeparator is false, the
returned path ends in that file name.
bAppendTrailingDirectorySeparator - [in]
If true, any file names are removed and a directory separator
is appended to the returned string.
Returns:
a path made left_side + right_side
Remarks:
This function manipulates string information.
This function does not look at storage media
to see if the paths currently exist.
*/
static const ON_wString CombinePaths(
const wchar_t* left_side,
bool bLeftSideContainsFileName,
const wchar_t* right_side,
bool bRightSideContainsFileName,
bool bAppendTrailingDirectorySeparator
);
/// <summary>
/// ON_FileSystemPath::PathId identifies a collection of commonly used directories.
/// Use ON_FileSystemPath::PlatformPath() to get the name of the directory.
/// </summary>
enum class PathId : unsigned int
{
///<summary>
/// The current user's home directory.
///</summary>
Unset = 0,
///<summary>
/// The current user's desktop directory.
///</summary>
DesktopDirectory = 1,
///<summary>
/// The current user's documents directory.
///</summary>
DocumentsDirectory = 2,
///<summary>
/// The current user's downloads directory.
///</summary>
DownloadsDirectory = 3,
///<summary>
/// The current user's home directory.
///</summary>
HomeDirectory = 4
};
/*
Parameters:
path_id - [in]
Specifies path to get.
Returns:
Requested path. If the path does not exist in the current context,
the empty string is returned.
*/
static const ON_wString PlatformPath(
ON_FileSystemPath::PathId path_id
);
ON_DEPRECATED_MSG("Use ON_FileSystem::PathExists")
static bool PathExists(
const char* path
);
ON_DEPRECATED_MSG("Use ON_FileSystem::PathExists")
static bool PathExists(
const wchar_t* path
);
ON_DEPRECATED_MSG("Use ON_FileSystem::IsDirectory")
static bool IsDirectory(
const char* path
);
ON_DEPRECATED_MSG("Use ON_FileSystem::IsDirectory")
static bool IsDirectory(
const wchar_t* path
);
ON_DEPRECATED_MSG("Use ON_FileSystem::IsFile")
static bool IsFile(
const char* path
);
ON_DEPRECATED_MSG("Use ON_FileSystem::IsFile")
static bool IsFile(
const wchar_t* path
);
};
class ON_CLASS ON_FileStream
{
public:
/*
Description:
Portable wrapper for C runtime fopen().
Parameters:
filename - [in]
mode - [in]
Remarks:
Use the ON_FileStream static functions for reading, writing,
seeking, position finding with the FILE pointer returned
by this function.
*/
static FILE* Open( const wchar_t* filename, const wchar_t* mode );
/*
Description:
Portable wrapper for C runtime fopen().
Parameters:
filename - [in]
mode - [in]
Remarks:
Use the ON_FileStream static functions for reading, writing,
seeking, position finding with the FILE pointer returned
by this function.
*/
static FILE* Open( const char* filename, const char* mode );
/*
Description:
Portable wrapper for C runtime fclose().
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
Returns:
0: successful
-1: null fp parameter
!= 0: fclose() failure code
*/
static int Close( FILE* fp );
/*
Returns:
True if the file is a 3dm archive.
*/
static bool Is3dmFile(
const wchar_t* file_path,
bool bAllow3dmbakExtension
);
/*
Returns:
True if the file is a 3dm archive.
*/
static bool Is3dmFile(
const char* file_path,
bool bAllow3dmbakExtension
);
/*
Description:
Open the file and seek to the location where the 3dm archive information begins.
Returns:
A file stream with the current position at the beginning of the 3dm archive.
nullptr if the file is not a 3dm archive.
*/
static FILE* Open3dmToRead(
const wchar_t* file_path
);
/*
Description:
Open the file and seek to the location where the 3dm archive information begins.
Returns:
A file stream with the current position at the beginning of the 3dm archive.
nullptr if the file is not a 3dm archive.
*/
static FILE* Open3dmToRead(
const char* file_path
);
/*
Description:
Portable wrapper for C runtime ftell().
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
Returns:
>= 0: current file position
-1: an error occurred
*/
static ON__INT64 CurrentPosition( FILE* fp );
/*
Description:
Portable wrapper for C runtime fseek(fp,offset,SEEK_CUR).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
offset - [in]
*/
static bool SeekFromCurrentPosition( FILE* fp, ON__INT64 offset );
/*
Description:
Portable wrapper for C runtime fseek(fp,offset,SEEK_SET).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
offset - [in]
*/
static bool SeekFromStart( FILE* fp, ON__INT64 offset );
/*
Description:
Portable wrapper for C runtime fseek(fp,offset,SEEK_END).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
offset - [in]
*/
static bool SeekFromEnd( FILE* fp, ON__INT64 offset );
/*
Description:
Portable wrapper for C runtime fseek(fp,offset,origin).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
offset - [in]
origin - [in]
SEEK_SET (0): seek from beginning of file.
SEEK_CUR (1): seek from current position of file pointer.
SEEK_END (2): seek from end of file.
*/
static bool Seek( FILE* fp, ON__INT64 offset, int orgin );
/*
Description:
Portable wrapper for C runtime fread(buffer,1,count,fp).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open()
count - [in]
number of bytes to read.
buffer - [out]
read bytes are stored in this buffer
Returns:
number of bytes read
*/
static ON__UINT64 Read( FILE* fp, ON__UINT64 count, void* buffer );
/*
Description:
Portable wrapper for C runtime fwrite(buffer,1,count,fp).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open()
count - [in]
number of bytes to write
buffer - [in]
data to be written
Returns:
number of bytes written.
*/
static ON__UINT64 Write( FILE* fp, ON__UINT64 count, const void* buffer );
/*
Description:
Portable wrapper for C runtime fflush(fp).
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
Returns:
true if flush was successful. False if an error occurred.
*/
static bool Flush( FILE* fp );
/*
Description:
Portable wrapper for C runtime fstat().
Parameters:
fp - [in]
FILE pointer returned by ON_FileStream::Open().
file_size - [out]
If file_size is not null, the the size of the file
in bytes returned here
file_metadata_last_modified_time - [out]
If file_metadata_last_modified_time is not null, then the time the
file's metadata (owner, permissions, ...) were last modified is returned
here as the number of seconds since midnight January 1, 1970.
file_contents_last_modified_time - [out]
If file_contents_last_modified_time is not null, then the time the
file's contents were last modified is returned here as the number of
seconds since midnight January 1, 1970.
Returns:
true if the query was successful. False if an error occurred.
*/
static bool GetFileInformation(
FILE* fp,
ON__UINT64* file_size,
ON__UINT64* file_metadata_last_modified_time,
ON__UINT64* file_contents_last_modified_time
);
static bool GetFileInformation(
const wchar_t* file_name,
ON__UINT64* file_size,
ON__UINT64* file_metadata_last_modified_time,
ON__UINT64* file_contents_last_modified_time
);
static bool GetFileInformation(
const char* file_name,
ON__UINT64* file_size,
ON__UINT64* file_metadata_last_modified_time,
ON__UINT64* file_contents_last_modified_time
);
};
class ON_CLASS ON_ContentHash
{
public:
static const ON_ContentHash Unset;
public:
ON_ContentHash() = default;
~ON_ContentHash() = default;
ON_ContentHash(const ON_ContentHash&) = default;
ON_ContentHash& operator=(const ON_ContentHash&) = default;
/*
Description:
Create an ON_ContentHash class with the specified size, hash and times.
Parameters:
sha1_name_hash - [in]
The SHA-1 hash of the name (typically a full path file name).
When the content is identified by a file name in a file system,
use ON_SHA1_Hash::FileSystemPathHash() to calculate this value.
byte_count - [in]
number of bytes in the content.
sha1_content_hash - [in]
The SHA-1 hash of the content (typically a buffer or file).
You may use ON_SHA1_Has::FileContentHash() or ON_SHA1_Hash::BufferContentHash()
to calculate this value.
hash_time - [in]
The time the sha1_hash was calculated in seconds since January 1, 1970 UCT.
If 0 is passed in, the current time is used.
content_last_modified_time - [in]
Pass 0 if not known.
The time the hashed information that was last modified in seconds since January 1, 1970 UCT.
If content_last_modified_time > hash_time, then 0 is used.
Returns:
An ON_ContentHash with size and SHA-1 hash and times set from the parameters,
*/
static ON_ContentHash Create(
ON_SHA1_Hash sha1_name_hash,
ON__UINT64 byte_count,
ON_SHA1_Hash sha1_content_hash,
ON__UINT64 hash_time,
ON__UINT64 content_last_modified_time
);
/*
Description:
Create an ON_ContentHash from a memory buffer.
Parameters:
sha1_name_hash - [in]
A SHA-1 hash of the name associated with this content.
If the buffer has no name, pass ON_SHA1_Hash::ZeroDigest.
If the buffer has an empty name, pass ON_SHA1_Hash::EmptyContentHash.
buffer - [in]
byte_count - [in]
number of bytes in buffer[]
Returns:
An ON_ContentHash with size and SHA-1 hash calculated from the parameters,
hash time = now, and content last modified time = 0.
*/
static ON_ContentHash CreateFromBuffer(
ON_SHA1_Hash sha1_name_hash,
const void* buffer,
size_t byte_count
);
/*
Description:
Create an ON_ContentHash from a file stream.
Parameters:
sha1_file_name_hash - [in]
A SHA-1 hash of the file name associated with fp.
Use ON_SHA1_Has::FileSystemPathHash() to create the value.
If the name is not known, pass ON_SHA1_Hash::ZeroDigest.
fp - [in] pointer to a file opened with ON:FileOpen(...,"rb")
Returns:
An ON_ContentHash with size and SHA-1 hash and times set from the file,
hash time = now, and content last modified time set from the file system
information returned by ON_FileStream::GetFileInformation().
*/
static ON_ContentHash CreateFromFile(
ON_SHA1_Hash sha1_file_name_hash,
FILE* fp
);
/*
Description:
Create an ON_ContentHash from a file stream.
Parameters:
filename - [in] name of file.
Returns:
An ON_ContentHash with size and SHA-1 hash and times set from the file,
hash time = now, and content last modified time set from the file system
information returned by ON_FileStream::GetFileInformation().
*/
static ON_ContentHash CreateFromFile(
const wchar_t* filename
);
static ON_ContentHash CreateFromFile(
const char* filename
);
/*
Returns:
True if the SHA-1 hash has been set.
*/
bool IsSet() const;
/*
Returns:
True if the SHA-1 hash is not set.
*/
bool IsNotSet() const;
/*
Returns:
Number of bytes in the content (typically a file or buffer).
*/
ON__UINT64 ByteCount() const;
/*
Returns:
Time the hash SHA-1 hash was calculated in seconds since January 1, 1970 UCT.
*/
ON__UINT64 HashCalculationTime() const;
/*
Returns:
Time the hashed content was last modified in seconds since January 1, 1970 UCT.
0 is returned if this time is not known.
This time should be used for important decisions as a last resort.
When hash values differ, this time may be considered to
which content is newer (or most recently copied).
Unfortunately, in many cases this time is often unknown and incorrectly set.
For example, some file systems set the last modified time of a copy of
an "old" file to the time the copy was created. Thus a copy of "old" content
may appear to be newer than "new" content that has not been copied.
*/
ON__UINT64 ContentLastModifiedTime() const;
/*
Returns:
SHA-1 hash of the name (typically a full path file name).
*/
ON_SHA1_Hash NameHash() const;
/*
Returns:
SHA-1 hash of the content (typically a buffer or file).
*/
ON_SHA1_Hash ContentHash() const;
/*
Description:
Test a buffer to see if it has a matching size and SHA-1 hash.
Parameters:
buffer - [in]
byte_count - [in]
number of bytes in buffer[]
Returns:
True if the buffer has a matching byte_count and SHA-1 hash.
*/
bool IsSameBufferContent(
const void* buffer,
size_t byte_count
) const;
/*
Description:
Test a file to see if it has a matching size and SHA-1 hash.
Parameters:
fp - [in] pointer to file opened with ON::OpenFile(...,"rb")
bSkipTimeCheck - [in] if true, the time of last
modification is not checked.
Returns:
True if the file existes, can be read, and has a matching byte_count
and SHA-1 hash.
*/
bool IsSameFileContent(
FILE* fp
) const;
/*
Description:
Test a file to see if it has a matching size and SHA-1 content hash.
Parameters:
filename - [in]
Returns:
True if the file exists, can be read, and has a matching byte_count
and SHA-1 content hash.
*/
bool IsSameFileContent(
const wchar_t* filename
) const;
bool IsSameFileContent(
const char* filename
) const;
/// <summary>
/// ON_ContentHash::Compare are the possible results of calling ON_ContentHash::CompareFile().
/// </summary>
enum class CompareResult : unsigned char
{
/// <summary>
/// Not set. This value is never returned by ON_ContentHash::CheckFile().
/// </summary>
Unset = 0,
/// <summary>
/// File exists and its size and content matches the information
/// used to set the content hash.
/// </summary>
EqualContent = 1,
/// <summary>
/// File exists and its size or content differs from the information
/// used to set the content hash. Unable to reliably determine which
/// is newer.
/// </summary>
DifferentContent = 2,
/// <summary>
/// File exists and its size or content differs from the information
/// used to set the content hash. The file's laste modified time
/// is older than ContentLastModifiedTime().
/// </summary>
DifferentContentFileIsOlder = 3,
/// <summary>
/// File exists and its size or content differs from the information
/// used to set the content hash. The file's last modified time
/// is newer than ContentLastModifiedTime().
/// </summary>
ContentDifferentFileIsNewer = 4,
/// <summary>
/// File does not exist.
/// </summary>
FileDoesNotExist = 5,
/// <summary>
/// File cannot be opened, read, or some other file system issue prevents checking.
/// </summary>
FileSystemFailure = 6
};
static ON_ContentHash::CompareResult CompareResultFromUnsigned(
unsigned int compare_result_as_unsigned
);
/*
Description:
Compare the information used to set this content hash with
the contents of the file.
Parameters:
file_path - [in]
bFastCompare - [in]
If bFastCompare is true and the file_path, create time, last modified time, and size
exactly match the values in ON_ContentHash, then
ON_ContentHash::CompareResult::EqualContent is returned
without performing the expensive SHA1 test on the file's content.
If bFastCompare is false, the SHA-1 hash of the file's content will be
calculated and compared before ON_ContentHash::CompareResult::EqualContent
is returned.
Returns:
Result of compare test as a ON_ContentHash::CompareResult enum.
ON_ContentHash::CompareResult::DifferentContentFileIsOlder means file_path content is different and older than "this".
ON_ContentHash::CompareResult::DifferentContentFileIsNewer means file_path content is different and newer than "this".
*/
ON_ContentHash::CompareResult Compare(
const wchar_t* file_path,
bool bFastTest
) const;
/*
Description:
Compare the byte count and SHA-1 content hash.
Parameters:
file_content_hash - [in]
ON_ContentHash to compare against this one.
Returns:
Result of compare test as a ON_ContentHash::CompareResult enum.
ON_ContentHash::CompareResult::DifferentContentFileIsOlder means file_content_hash is different and older than "this".
ON_ContentHash::CompareResult::DifferentContentFileIsNewer means file_content_hash is different and newer than "this".
*/
ON_ContentHash::CompareResult Compare(
ON_ContentHash file_content_hash
) const;
/*
Returns:
true if a and b have identical ByteCount() and SHA-1 content hash values.
*/
static bool EqualContent(
const ON_ContentHash& a,
const ON_ContentHash& b
);
/*
Returns:
true if a and b have different ByteCount() or SHA-1 content hash values.
*/
static bool DifferentContent(
const ON_ContentHash& a,
const ON_ContentHash& b
);
/*
Description:
Compares content byte count and content SHA-1
*/
static int CompareContent(
const ON_ContentHash& a,
const ON_ContentHash& b
);
/*
Description:
Compares all fields
*/
static int Compare(
const ON_ContentHash& a,
const ON_ContentHash& b
);
/*
Parameters:
filename - [in]
Returns:
True if the file exists, has size > 0, has the same name, same size, and same last modified time
than this content hash.
False otherwise.
Remarks:
Faster than the ON_ContentHash::EqualContent() and reliable if this content
hash was set on the same file system.
Unreliable if the file system does not correctly set last modified times
or the file was modified less than 2 seconds before the call.
*/
bool EqualFileNameSizeAndTime(
const wchar_t* filename
) const;
bool Write(
class ON_BinaryArchive& archive
) const;
bool Read(
class ON_BinaryArchive& archive
);
void Dump(
class ON_TextLog& text_log
) const;
private:
// Number of bytes in the buffer or file
ON__UINT64 m_byte_count = 0;
// Time this hash was set (always > 0 if this ON_ContentHash is set).
ON__UINT64 m_hash_time = 0; // number of seconds since Jan 1, 1970, UCT
// Time the content was last modified.
// This time is often unknown, or set incorrectly.
ON__UINT64 m_content_time = 0; // number of seconds since Jan 1, 1970, UCT
// SHA-1 hash of the content name (file name or other assigned name)
ON_SHA1_Hash m_sha1_name_hash = ON_SHA1_Hash::ZeroDigest;
// SHA-1 hash of the content (buffer or file).
ON_SHA1_Hash m_sha1_content_hash = ON_SHA1_Hash::ZeroDigest;
};
class ON_CLASS ON_FileReference
{
public:
static const ON_FileReference Unset;
#pragma region RH_C_SHARED_ENUM [ON_FileReference::Status] [Rhino.FileIO.FileReferenceStatus] [int]
///<summary>Enumerates a list of file statuses.</summary>
enum class Status : unsigned int
{
/// <summary>
/// Status of a the full path is not known.
/// </summary>
Unknown = 0,
/// <summary>
/// Full path is valid.
/// </summary>
FullPathValid = 1,
/// <summary>
/// Unable to locate file.
/// </summary>
FileNotFound = 2
};
#pragma endregion
static int Compare(
const ON_FileReference& a,
const ON_FileReference& b
);
static ON_FileReference::Status StatusFromUnsigned(
unsigned int full_path_status_as_unsigned
);
ON_FileReference() = default;
~ON_FileReference() = default;
ON_FileReference(const ON_FileReference&) = default;
ON_FileReference& operator=(const ON_FileReference&) = default;
ON_FileReference(
const wchar_t* full_path,
const wchar_t* relative_path,
ON_ContentHash content_hash,
ON_FileReference::Status full_path_status
);
static ON_FileReference CreateFromFullPath(
const wchar_t* full_path,
bool bSetContentHash,
bool bSetFullPathStatus
);
#pragma region RH_C_SHARED_ENUM [ON_FileReference::FindFilePreference] [Rhino.FileIO.FileFindPreference] [int]
///<summary>Defines options for file search.</summary>
enum class FindFilePreference : unsigned char
{
///<summary>The choice is not defined.</summary>
None = 0,
///<summary>File name exists in FullPath().</summary>
FullPath = 1,
///<summary>File name exists in base path + RelativePath().</summary>
RelativePath = 2,
///<summary>File name exists in base path directory.</summary>
BasePath = 3,
///<summary>File with matching content exists.</summary>
ContentMatch = 4,
///<summary>Most recently modified file.</summary>
MostRecent = 5
};
#pragma endregion
/*
Description:
Uses the full path, relative path and parameter information to find a
full path to a file that exists.
Parameters:
base_path - [in]
If base_path and RelativePath() are not empty, then path base_path+RelativePath().
If base_path is not empty, then base_path + filename is considered.
bBasePathIncludesFileName - [in]
True if base_path contains a file name that must be removed to get a directory path.
first_choice - [in]
When multiple files are found in different locations, the first_choice, second_choice,
third_choice, forth_choice, and fifth_choice parameters are used to select which file
is returned.
second_choice - [in]
When multiple files are found in different locations, the first_choice, second_choice,
third_choice, forth_choice, and fifth_choice parameters are used to select which file
is returned.
third_choice - [in]
When multiple files are found in different locations, the first_choice, second_choice,
third_choice, forth_choice, and fifth_choice parameters are used to select which file
is returned.
forth_choice - [in]
When multiple files are found in different locations, the first_choice, second_choice,
third_choice, forth_choice, and fifth_choice parameters are used to select which file
is returned.
fifth_choice - [in]
When multiple files are found in different locations, the first_choice, second_choice,
third_choice, forth_choice, and fifth_choice parameters are used to select which file
is returned.
full_path - [out]
A full path to a file that exists.
If FullPath() and base_path+RelativePath() resolve to different files,
the content hash information is used to select the file.
Returns:
If the file is found, then the returned ON_FileReference::FindFilePreference enum value
indicates why it was selected.
If the file is not found, then ON_FileReference::FindFilePreference::None is returned
and full_path is empty.
Remarks:
The locations FullPath(), base_path+RelativePath(), and base_path+FileName() are tested.
If multiple files are found, first_choice, second_choice, third_choice, forth_choice,
and fifth_choice are used to select which file is returned.
*/
ON_FileReference::FindFilePreference FindFile(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
ON_FileReference::FindFilePreference first_choice,
ON_FileReference::FindFilePreference second_choice,
ON_FileReference::FindFilePreference third_choice,
ON_FileReference::FindFilePreference forth_choice,
ON_FileReference::FindFilePreference fifth_choice,
ON_wString& found_file_full_path
) const;
/*
Description:
Uses the full path, relative path and parameter information to find a
full path to a file that exists.
Parameters:
base_path - [in]
If base_path and RelativePath() are not empty, then path base_path+RelativePath().
If base_path is not empty, then base_path + filename is considered.
bBasePathIncludesFileName - [in]
True if base_path contains a file name that must be removed to get a directory path.
Returns:
If the file is found, then the returned ON_FileReference::FindFilePreference enum value
indicates why it was selected.
If the file is not found, then ON_FileReference::FindFilePreference::None is returned
and full_path is empty.
Remarks:
The locations FullPath(), base_path+RelativePath(), and base_path+FileName() are tested.
If multiple files are found, the returned file is selected in the order
relative path, full path, content match, base path and most recently modified.
If you prefer a different order, use the version of ON_FileReference::FindFile
with 5 ON_FileReference::FindFilePreference parameters.
*/
ON_FileReference::FindFilePreference FindFile(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
ON_wString& found_file_full_path
) const;
/*
Description:
The search for the file is identical to the one performed by find file.
If a file is found, the full path setting in this reference is updated.
*/
ON_FileReference::FindFilePreference FindFileAndUpdateReference(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
ON_FileReference::FindFilePreference first_choice,
ON_FileReference::FindFilePreference second_choice,
ON_FileReference::FindFilePreference third_choice,
ON_FileReference::FindFilePreference forth_choice,
ON_FileReference::FindFilePreference fifth_choice,
bool bUpdateContentHash,
ON_wString& found_file_full_path
);
/*
Description:
The search for the file is identical to the one performed by find file.
If a file is found, the full path setting in this reference is updated.
*/
ON_FileReference::FindFilePreference FindFileAndUpdateReference(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
bool bUpdateContentHash,
ON_wString& found_file_full_path
);
ON_FileReference::FindFilePreference FindFileAndUpdateReference(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
bool bUpdateContentHash
);
/*
Returns:
True if FullPath() is not empty.
*/
bool IsSet() const;
/*
Returns:
True if FullPath() is empty.
*/
bool IsNotSet() const;
/*
Parameters:
bUseArchiveBasePath - [in]
If bUseArchiveBasePath is true and a file is being written, then the
base path of the file being written use used as the base path to
calculate the relative path.
If bUseArchiveBasePath is false, then the current value of RelativePath()
is saved in the archive.
*/
bool Write(
bool bUseArchiveDirectoryAsBasePath,
ON_BinaryArchive& archive
) const;
/*
Parameters:
base_path - [in]
If base_path is not empty, then the relative path saved
in the archive will be calculated from FullPath() and base_path.
If base_path is nullptr or empty, then RelativePath() is saved in
the archive.
*/
bool Write(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
ON_BinaryArchive& archive
) const;
/*
Remarks:
Calling Read() sets m_full_path_status = ON_FileReference::Status::Unknown,
even if that was not the status when Write() was called.
*/
bool Read(
ON_BinaryArchive& archive
);
void Dump(
class ON_TextLog& text_log
) const;
unsigned int SizeOf() const;
const ON_wString& FullPath() const;
const wchar_t* FullPathAsPointer() const;
void SetFullPath(
const wchar_t* full_path,
bool bSetContentHash
);
void SetFullPath(
const char* full_path,
bool bSetContentHash
);
void ClearFullPath();
const ON_wString& RelativePath() const;
const wchar_t* RelativePathAsPointer() const;
void SetRelativePath(
const wchar_t* relative_path
);
void SetRelativePath(
const char* relative_path
);
void SetRelativePathFromBasePath(
const wchar_t* base_path,
bool bBasePathContainsFileName
);
void SetRelativePathFromBasePath(
const char* base_path,
bool bBasePathContainsFileName
);
void ClearRelativePath();
/*
Returns:
File content hash. This value is persistent, saved in 3dm archive,
and could have been calculated a long time ago on a different computer.
*/
const ON_ContentHash& ContentHash() const;
void SetContentHash(
ON_ContentHash content_hash
);
void ClearContentHash();
bool UpdateContentHash();
/*
Returns:
Parameters:
recent_time - [in]
The time, in number of seconds since January 1, 1970 UTC, to use
when deciding what content hashes can be considered recent.
If recent_time is 0 or in the future, then the current value of
ON_SecondsSinceJanOne1970UTC() is used.
Typically this parameter is the value of ON_SecondsSinceJanOne1970UTC()
at the beginning of a calculation durint which any referenced files will
not be changed.
Returns:
A file content hash value calculated on or after a specified time in the current
instance of the application. This value is used to detect changed files
in the current instance of the application. It is cached for performance reasons.
This value is never saved in 3dm files.
*/
const ON_ContentHash& RecentContentHash(
ON__UINT64 recent_time
) const;
/*
Returns:
ON_SHA1_Hash::FileSystemPathHash(FullPath());
Remarks:
The value of the hash is saved in a runtime cache so
using this function when comparing paths is efficient
when multiple compares are required.
See Also:
ON_NameHash::CreateFilePathHash( ON_FileReference& file_reference );
*/
const ON_SHA1_Hash& FullPathHash() const;
ON_FileReference::Status FullPathStatus() const;
void SetFullPathStatus(
ON_FileReference::Status full_path_status
);
ON_UUID EmbeddedFileId() const;
void SetEmbeddedFileId(
ON_UUID embedded_file_id
);
private:
ON_wString m_full_path;
ON_wString m_relative_path;
// If the referenced file is saved in the model as an embedded file,
// the ON_BinaryArchive read code sets m_embedded_file_id
// at read time.
mutable ON_UUID m_embedded_file_id = ON_nil_uuid;
// file content hash. Can be calculated long ago, on a different computer,
// and is saved in 3dm archived.
ON_ContentHash m_content_hash; // File content hash.
mutable ON_ContentHash m_recent_content_hash;
// m_full_path_hash is cached runtime information. The value is not saved
// in .3dm archives. It is calculated on demand.
mutable ON_SHA1_Hash m_full_path_hash = ON_SHA1_Hash::EmptyContentHash; // File path hash.
ON_FileReference::Status m_full_path_status = ON_FileReference::Status::Unknown;
private:
ON_FileReference::FindFilePreference Internal_FindFile(
const wchar_t* base_path,
bool bBasePathIncludesFileName,
const ON_FileReference::FindFilePreference* file_preference,
unsigned int file_preference_count,
ON_wString& found_file_full_path,
ON_ContentHash* found_file_content_hash
) const;
};
/*
Description:
Iterates through every item in a file system directory.
*/
class ON_CLASS ON_FileIterator
{
public:
ON_FileIterator() = default;
~ON_FileIterator();
private:
ON_FileIterator(const ON_FileIterator&) = delete;
ON_FileIterator& operator=(const ON_FileIterator&) = delete;
public:
//////////////////////////////////////////////////////////////////////////////////
//
// Iteratation initialization tools
//
/*
Description:
Initialize where the search should occur.
Parameters:
directory_name - [in]
The directory to look in.
item_name_filter - [in]
If this parameter is null, then the iteration
includes all names in the directory.
The item name to search for. This parameter can
include wildcard characters, such as an
asterisk (*) or a question mark (?). For example,
"\rootdir\subdir\*.*" will iterate all files in
the \rootdir\subdir\ directory.
Returns:
true:
The iterator is set to the first item.
false:
There are no matching items.
Remarks:
Calling FirstItem() is equivalent to calling Initialize() and then calling NextItem().
*/
bool Initialize(
const wchar_t* directory_name
);
bool Initialize(
const wchar_t* directory_name,
const wchar_t* item_name_filter
);
bool Initialize(
const char* directory_name
);
bool Initialize(
const char* directory_name,
const char* item_name_filter
);
//////////////////////////////////////////////////////////////////////////////////
//
// Iteratation iteration tools
//
/*
Description:
Find the first matching item in the directory.
Example:
// Iterate through the files in a directory named "\rootdir\subdir"
ON_FileIterator fit;
fit.Initialize("\\rootdir\\subdir");
for ( bool bHaveItem = fit.FirstItem(); bHaveItem; bHaveItem = fit.NextItem() )
{
if ( fit.CurrentFileIsDirectory() )
continue;
ON_String fullpath = fit.CurrentItemFullPathName();
FILE* fp = ON_FileStream::Open(fullpath,"rb");
if ( 0 == fp )
{
continue;
}
...
ON_FileStream::Close(fp);
fp = 0;
}
}
Returns:
true:
The iterator is set to the first item.
false:
There are no matching items.
*/
bool FirstItem();
/*
Description:
Find the next matching item in the directory.
Returns:
true:
The iterator was advanced to the next item.
false:
There are no more matching items.
*/
bool NextItem();
/*
Description:
Reset this ON_FileIterator so it can be used again.
*/
void Reset();
//////////////////////////////////////////////////////////////////////////////////
//
// Current item query
//
/*
Returns:
Current file or directory name in the directory being iterated.
Use CurrentFullPathItemName() to get the full path name.
*/
const ON_wString CurrentItemName() const;
/*
Returns:
The name of the directory being iterated.
*/
const ON_wString DirectoryName() const;
/*
Returns:
If the current item is a file, then the size of the file in bytes is returned.
If the current item is a directory, then 0 is returned.
*/
ON__UINT64 CurrentItemSize() const;
/*
Returns
true if the current item is a directory.
*/
bool CurrentItemIsDirectory() const;
/*
Returns
true if the current item is a file.
*/
bool CurrentItemIsFile() const;
/*
Returns
true if the current file or directory is hidden.
This means its name begins with a '.' or it's
Windows hidden attribute is true.
*/
bool CurrentItemIsHidden() const;
const ON_wString CurrentItemFullPathName() const;
/*
Returns:
File last modified time in seconds since January 1, 1970
Remarks:
The times returned by ON_FileIterator can differ from the time
returned by ON_FileStream::GetFileInformation().
*/
ON__UINT64 CurrentItemLastModifiedTime() const;
/*
Returns:
Number of matching items iterated through.
*/
ON__UINT64 CurrentItemCount() const;
private:
ON__UINT32 m_state = 0; // 0 unset, 1=initialized, 2 = itereation in progress. 3 = iteration finished.
ON__UINT32 m_reserved = 0;
ON_wString m_directory; // directory passed to Initialize() or FirstItem
ON_wString m_item_name_filter; // item_name_filter passed to Initialize() or FirstItem
ON_wString m_item_name; // Current item name.
// cached full path name
// m_directory + directory separator + m_item_name
// (length = 0 if it is not set)
mutable ON_wString m_full_path_name;
ON__UINT64 m_count = 0; // number of items iterated through so far
class ON_DirectoryIteratorImpl* m_impl = nullptr;
};
class ON_CLASS ON_UnicodeTextFile final
{
public:
enum class Modes { Read, Write };
enum class Types { Unknown, UTF8, UTF16 };
ON_UnicodeTextFile(Types type = Types::Unknown);
~ON_UnicodeTextFile();
bool Open(const wchar_t* filename, Modes mode);
bool Close(void);
bool ReadString(ON_wString& s);
bool WriteString(const wchar_t* s);
ON_UnicodeTextFile(const ON_UnicodeTextFile&) = delete;
const ON_UnicodeTextFile& operator = (const ON_UnicodeTextFile&) = delete;
private:
class ON_UnicodeTextFilePrivate* _private;
};
#endif