Go to the documentation of this file.
16 #include "llvm/Config/config.h"
17 #include "llvm/Config/llvm-config.h"
26 #if !defined(_MSC_VER) && !defined(__MINGW32__)
45 return LLVM_WINDOWS_PREFER_FORWARD_SLASH ? Style::windows_slash
46 : Style::windows_backslash;
49 inline const char *separators(
Style style) {
55 inline char preferred_separator(
Style style) {
56 if (real_style(style) == Style::windows)
73 if (path.
size() >= 2 &&
74 std::isalpha(
static_cast<unsigned char>(path[0])) && path[1] ==
':')
99 return str.
size() - 1;
140 size_t end_pos = filename_pos(path, style);
142 bool filename_was_sep =
146 size_t root_dir_pos = root_dir_start(path, style);
147 while (end_pos > 0 &&
152 if (end_pos == root_dir_pos && !filename_was_sep) {
155 return root_dir_pos + 1;
169 static std::error_code
180 for (
int Retries = 128; Retries > 0; --Retries) {
195 return std::error_code();
201 return std::error_code();
214 return std::error_code();
229 i.Component = find_first_component(path, style);
238 i.Position = path.
size();
243 assert(Position < Path.size() &&
"Tried to increment past end!");
249 if (Position == Path.size()) {
270 while (Position != Path.size() &&
is_separator(Path[Position],
S)) {
275 if (Position == Path.size() &&
Component !=
"/") {
283 size_t end_pos = Path.find_first_of(separators(
S), Position);
284 Component = Path.slice(Position, end_pos);
290 return Path.begin() ==
RHS.Path.begin() && Position ==
RHS.Position;
294 return Position -
RHS.Position;
300 I.Position = Path.size();
309 I.Component = Path.substr(0, 0);
315 size_t root_dir_pos = root_dir_start(Path,
S);
318 size_t end_pos = Position;
319 while (end_pos > 0 && (end_pos - 1) != root_dir_pos &&
324 if (Position == Path.size() && !Path.empty() &&
333 size_t start_pos = filename_pos(Path.substr(0, end_pos),
S);
334 Component = Path.slice(start_pos, end_pos);
335 Position = start_pos;
340 return Path.begin() ==
RHS.Path.begin() &&
Component ==
RHS.Component &&
341 Position ==
RHS.Position;
345 return Position -
RHS.Position;
352 b->size() > 2 &&
is_separator((*
b)[0], style) && (*b)[1] == (*b)[0];
355 if (has_net || has_drive) {
358 return path.
substr(0,
b->size() + pos->size());
377 b->size() > 2 &&
is_separator((*
b)[0], style) && (*b)[1] == (*b)[0];
380 if (has_net || has_drive) {
394 b->size() > 2 &&
is_separator((*
b)[0], style) && (*b)[1] == (*b)[0];
397 if ((has_net || has_drive) &&
426 if (!
a.isTriviallyEmpty()) components.push_back(
a.toStringRef(a_storage));
427 if (!
b.isTriviallyEmpty()) components.push_back(
b.toStringRef(b_storage));
428 if (!
c.isTriviallyEmpty()) components.push_back(
c.toStringRef(c_storage));
429 if (!
d.isTriviallyEmpty()) components.push_back(
d.toStringRef(d_storage));
431 for (
auto &component : components) {
433 !path.empty() &&
is_separator(path[path.size() - 1], style);
436 size_t loc = component.find_first_not_of(separators(style));
444 bool component_has_sep =
445 !component.empty() &&
is_separator(component[0], style);
446 if (!component_has_sep &&
449 path.push_back(preferred_separator(style));
452 path.
append(component.begin(), component.end());
468 size_t end_pos = parent_path_end(path, style);
471 return path.
substr(0, end_pos);
475 size_t end_pos = parent_path_end(
StringRef(path.begin(), path.size()), style);
487 size_t pos =
p.find_last_of(
'.');
492 if (ext.
size() > 0 && ext[0] !=
'.')
503 if (Path.size() <
Prefix.size())
505 for (
size_t I = 0,
E =
Prefix.size();
I !=
E; ++
I) {
508 if (SepPath != SepPrefix)
510 if (!SepPath && toLower(Path[
I]) != toLower(
Prefix[
I]))
515 return Path.startswith(
Prefix);
523 StringRef OrigPath(Path.begin(), Path.size());
528 if (OldPrefix.
size() == NewPrefix.
size()) {
535 (
Twine(NewPrefix) + RelPath).toVector(NewPath);
543 "path and result are not allowed to overlap!");
554 for (
char &Ch : Path)
556 Ch = preferred_separator(style);
557 if (Path[0] ==
'~' && (Path.size() == 1 ||
is_separator(Path[1], style))) {
560 PathHome.
append(Path.begin() + 1, Path.end());
570 return std::string(path);
572 std::string
s = path.
str();
584 if ((fname.
size() == 1 && fname ==
".") ||
585 (fname.
size() == 2 && fname ==
".."))
587 return fname.
substr(0, pos);
595 if ((fname.
size() == 1 && fname ==
".") ||
596 (fname.
size() == 2 && fname ==
".."))
605 return value ==
'\\';
610 if (real_style(style) == Style::windows)
678 return rootDir && rootName;
692 if (
p.size() >= 2 && (
p[0] &&
p[1] ==
':'))
705 while (Path.size() > 2 && Path[0] ==
'.' &&
is_separator(Path[1], style)) {
706 Path = Path.substr(2);
708 Path = Path.substr(1);
717 style = real_style(style);
718 StringRef remaining(the_path.data(), the_path.size());
719 bool needs_change =
false;
724 bool absolute = !root.
empty();
730 while (!remaining.
empty()) {
731 size_t next_slash = remaining.
find_first_of(separators(style));
733 next_slash = remaining.
size();
738 if (!remaining.
empty()) {
739 needs_change |= remaining.
front() != preferred_separator(style);
743 needs_change |= remaining.
empty();
747 if (component.
empty() || component ==
".") {
749 }
else if (remove_dot_dot && component ==
"..") {
753 if (!components.empty() && components.back() !=
"..") {
754 components.pop_back();
755 }
else if (!absolute) {
756 components.push_back(component);
759 components.push_back(component);
766 needs_change |= root != buffer;
772 if (!components.empty()) {
773 buffer += components[0];
775 buffer += preferred_separator(style);
779 the_path.
swap(buffer);
792 Result =
Status.getUniqueID();
793 return std::error_code();
799 Model.toVector(ModelStorage);
807 ModelStorage.
swap(TDir);
811 ResultPath = ModelStorage;
812 ResultPath.push_back(0);
813 ResultPath.pop_back();
816 for (
unsigned i = 0,
e = ModelStorage.size();
i !=
e; ++
i) {
817 if (ModelStorage[
i] ==
'%')
841 static std::error_code
848 "Model must be a simple filename.");
854 static std::error_code
858 const char *Middle = Suffix.
empty() ?
"-%%%%%%" :
"-%%%%%%.";
919 current_directory.
toVector(current_dir);
922 if (!rootName && !rootDirectory) {
926 path.
swap(current_dir);
930 if (!rootName && rootDirectory) {
935 path.
swap(curDirRootName);
939 if (rootName && !rootDirectory) {
946 path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath);
992 const size_t BufSize = 4096;
993 char *Buf =
new char[BufSize];
994 int BytesRead = 0, BytesWritten = 0;
996 BytesRead =
read(ReadFD, Buf, BufSize);
1000 BytesWritten =
write(WriteFD, Buf, BytesRead);
1001 if (BytesWritten < 0)
1003 BytesRead -= BytesWritten;
1005 if (BytesWritten < 0)
1010 if (BytesRead < 0 || BytesWritten < 0)
1011 return std::error_code(errno, std::generic_category());
1012 return std::error_code();
1017 int ReadFD, WriteFD;
1020 if (std::error_code EC =
1050 constexpr
size_t BufSize = 4096;
1051 std::vector<uint8_t> Buf(BufSize);
1054 BytesRead =
read(FD, Buf.data(), BufSize);
1061 return std::error_code(errno, std::generic_category());
1082 return s.type() != file_type::status_error;
1087 if (
status(Path, st, Follow))
1088 return file_type::status_error;
1093 return status.type() == file_type::directory_file;
1098 if (std::error_code ec =
status(path, st))
1101 return std::error_code();
1105 return status.type() == file_type::regular_file;
1110 if (std::error_code ec =
status(path, st))
1113 return std::error_code();
1117 return status.type() == file_type::symlink_file;
1122 if (std::error_code ec =
status(path, st,
false))
1125 return std::error_code();
1136 if (std::error_code EC =
status(Path, FileStatus))
1139 return std::error_code();
1146 this->Path = std::string(PathStr.
str());
1156 return Status.permissions();
1160 assert(Mapping &&
"Mapping failed but used anyway!");
1164 char *mapped_file_region::data()
const {
1165 assert(Mapping &&
"Mapping failed but used anyway!");
1166 return reinterpret_cast<char *
>(Mapping);
1169 const char *mapped_file_region::const_data()
const {
1170 assert(Mapping &&
"Mapping failed but used anyway!");
1171 return reinterpret_cast<const char *
>(Mapping);
1175 ssize_t ChunkSize) {
1177 size_t Size = Buffer.size();
1187 if (*ReadBytes == 0)
1198 #if defined(LLVM_ON_UNIX)
1214 return getMainExecutableImpl(Argv0, MainAddr);
1217 TempFile::TempFile(
StringRef Name,
int FD)
1226 RemoveOnClose =
Other.RemoveOnClose;
1227 Other.RemoveOnClose =
false;
1236 if (FD != -1 && close(FD) == -1) {
1237 std::error_code EC = std::error_code(errno, std::generic_category());
1245 bool Remove = RemoveOnClose;
1250 std::error_code RemoveEC;
1251 if (Remove && !TmpName.empty()) {
1268 auto H =
reinterpret_cast<HANDLE
>(_get_osfhandle(FD));
1269 std::error_code RenameEC =
1270 RemoveOnClose ? std::error_code() : setDeleteDisposition(
H,
false);
1271 bool ShouldDelete =
false;
1273 RenameEC = rename_handle(
H, Name);
1276 std::error_code(ERROR_NOT_SAME_DEVICE, std::system_category())) {
1278 ShouldDelete =
true;
1284 ShouldDelete =
true;
1287 setDeleteDisposition(
H,
true);
1292 std::error_code RenameEC =
fs::rename(TmpName, Name);
1306 if (close(FD) == -1) {
1307 std::error_code EC(errno, std::generic_category());
1320 auto H =
reinterpret_cast<HANDLE
>(_get_osfhandle(FD));
1321 if (std::error_code EC = setDeleteDisposition(
H,
false))
1328 if (close(FD) == -1) {
1329 std::error_code EC(errno, std::generic_category());
1341 if (std::error_code EC =
1347 auto H =
reinterpret_cast<HANDLE
>(_get_osfhandle(FD));
1348 bool SetSignalHandler =
false;
1349 if (std::error_code EC = setDeleteDisposition(
H,
true)) {
1350 Ret.RemoveOnClose =
true;
1351 SetSignalHandler =
true;
1354 bool SetSignalHandler =
true;
void createUniquePath(const Twine &Model, SmallVectorImpl< char > &ResultPath, bool MakeAbsolute)
Create a potentially unique file name but does not create it.
std::error_code openFileForWrite(const Twine &Name, int &ResultFD, CreationDisposition Disp=CD_CreateAlways, OpenFlags Flags=OF_None, unsigned Mode=0666)
Opens the file with the given name in a write-only or read-write mode, returning its open file descri...
static bool starts_with(StringRef Path, StringRef Prefix, Style style=Style::native)
StringRef take_front(size_t N=1) const
Return a StringRef equal to 'this' but with only the first N elements remaining.
Represents the result of a call to directory_iterator::status().
This is an optimization pass for GlobalISel generic memory operations.
bool replace_path_prefix(SmallVectorImpl< char > &Path, StringRef OldPrefix, StringRef NewPrefix, Style style=Style::native)
Replace matching path prefix with another path.
std::error_code current_path(SmallVectorImpl< char > &result)
Get the current path.
bool home_directory(SmallVectorImpl< char > &result)
Get the user's home directory.
StringRef extension(StringRef path, Style style=Style::native)
Get extension.
void update(ArrayRef< uint8_t > Data)
Updates the hash for the byte stream provided.
static std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, int &ResultFD, llvm::SmallVectorImpl< char > &ResultPath, FSEntity Type, sys::fs::OpenFlags Flags=sys::fs::OF_None)
StringRef getSingleStringRef() const
This returns the twine as a single StringRef.
reverse_iterator rbegin(StringRef path, Style style=Style::native)
Get reverse begin iterator over path.
char front() const
front - Get the first character in the string.
This currently compiles esp xmm0 movsd esp eax eax esp ret We should use not the dag combiner This is because dagcombine2 needs to be able to see through the X86ISD::Wrapper which DAGCombine can t really do The code for turning x load into a single vector load is target independent and should be moved to the dag combiner The code for turning x load into a vector load can only handle a direct load from a global or a direct load from the stack It should be generalized to handle any load from P
StringRef remove_leading_dotslash(StringRef path, Style style=Style::native)
Remove redundant leading "./" pieces and consecutive separators.
static constexpr size_t npos
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool is_absolute(const Twine &path, Style style=Style::native)
Is path absolute?
std::string convert_to_slash(StringRef path, Style style=Style::native)
Replaces backslashes with slashes if Windows.
static ErrorSuccess success()
Create a success value.
std::error_code getUniqueID(const Twine Path, UniqueID &Result)
std::error_code getPotentiallyUniqueTempFileName(const Twine &Prefix, StringRef Suffix, SmallVectorImpl< char > &ResultPath)
Get a unique temporary file name, not currently exisiting in the filesystem.
std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None, unsigned Mode=all_read|all_write)
Create a uniquely named file.
size_t find_first_of(char C, size_t From=0) const
Find the first character in the string that is C, or npos if not found.
The instances of the Type class are immutable: once they are created, they are never changed.
const_iterator end(StringRef path)
Get end iterator over path.
const_iterator begin(StringRef path, Style style=Style::native)
Get begin iterator over path.
OutputIt copy(R &&Range, OutputIt Out)
@ no_such_file_or_directory
std::error_code access(const Twine &Path, AccessMode Mode)
Can the file be accessed?
Tagged union holding either a T or a Error.
bool is_regular_file(const basic_file_status &status)
Does status represent a regular file?
static void replace(Module &M, GlobalVariable *Old, GlobalVariable *New)
void consumeError(Error Err)
Consume a Error without doing anything.
the resulting code requires compare and branches when and if * p
Expected< size_t > readNativeFile(file_t FileHandle, MutableArrayRef< char > Buf)
Reads Buf.size() bytes from FileHandle into Buf.
=0.0 ? 0.0 :(a > 0.0 ? 1.0 :-1.0) a
It looks like we only need to define PPCfmarto for these because according to these instructions perform RTO on fma s result
void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
void truncate(size_type N)
Like resize, but requires that N is less than size().
void final(MD5Result &Result)
Finishes off the hash and puts the result in result.
void make_preferred(SmallVectorImpl< char > &path, Style style=Style::native)
For Windows path styles, convert path to use the preferred path separators.
bool has_extension(const Twine &path, Style style=Style::native)
Has extension?
static unsigned GetRandomNumber()
Get the result of a process wide random number generator.
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
bool has_parent_path(const Twine &path, Style style=Style::native)
Has parent path?
(vector float) vec_cmpeq(*A, *B) C
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
std::error_code createUniqueDirectory(const Twine &Prefix, SmallVectorImpl< char > &ResultPath)
StringRef root_path(StringRef path, Style style=Style::native)
Get root path.
the resulting code requires compare and branches when and if the revised code is with conditional branches instead of More there is a byte word extend before each where there should be only and the condition codes are not remembered when the same two values are compared twice More LSR enhancements i8 and i32 load store addressing modes are identical int b
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
bool has_stem(const Twine &path, Style style=Style::native)
Has stem?
std::error_code create_directories(const Twine &path, bool IgnoreExisting=true, perms Perms=owner_all|group_all)
Create all the non-existent directories in path.
file_type get_file_type(const Twine &Path, bool Follow=true)
Does status represent a directory?
MutableArrayRef(T &OneElt) -> MutableArrayRef< T >
const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
file_type
An enumeration for the file system's view of the type.
the resulting code requires compare and branches when and if the revised code is with conditional branches instead of More there is a byte word extend before each where there should be only and the condition codes are not remembered when the same two values are compared twice More LSR enhancements i8 and i32 load store addressing modes are identical int int c
std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD, CreationDisposition Disp, OpenFlags Flags, unsigned Mode=0666)
Opens the file with the given name in a write-only or read-write mode, returning its open file descri...
Error readNativeFileToEOF(file_t FileHandle, SmallVectorImpl< char > &Buffer, ssize_t ChunkSize=DefaultReadChunkSize)
Reads from FileHandle until EOF, appending to Buffer in chunks of size ChunkSize.
void write(void *memory, value_type value, endianness endian)
Write a value to memory with a particular endianness.
void resize_for_overwrite(size_type N)
Like resize, but T is POD, the new values won't be initialized.
constexpr bool is_style_posix(Style S)
Check if S uses POSIX path rules.
constexpr bool empty() const
empty - Check if the string is empty.
void append(StringRef RHS)
Append from a StringRef.
bool is_other(const basic_file_status &status)
Does this status represent something that exists but is not a directory or regular file?
Represents the result of a call to sys::fs::status().
bool remove_dots(SmallVectorImpl< char > &path, bool remove_dot_dot=false, Style style=Style::native)
In-place remove any '.
bool isSingleStringRef() const
Return true if this twine can be dynamically accessed as a single StringRef value with getSingleStrin...
bool exists(const basic_file_status &status)
Does file exist?
std::error_code getPotentiallyUniqueFileName(const Twine &Model, SmallVectorImpl< char > &ResultPath)
Get a unique name, not currently exisiting in the filesystem.
StringRef get_separator(Style style=Style::native)
Return the preferred separator for this platform.
StringRef root_directory(StringRef path, Style style=Style::native)
Get root directory.
void replace_extension(SmallVectorImpl< char > &path, const Twine &extension, Style style=Style::native)
Replace the file extension of path with extension.
multiplies can be turned into SHL s
@ OF_Delete
The returned handle can be used for deleting the file.
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
void toVector(SmallVectorImpl< char > &Out) const
Append the concatenated string into the given SmallString or SmallVector.
bool status_known(const basic_file_status &s)
Is status available?
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
StringRef parent_path(StringRef path, Style style=Style::native)
Get parent path.
bool is_symlink_file(const basic_file_status &status)
Does status represent a symlink file?
bool operator==(uint64_t V1, const APInt &V2)
void make_absolute(const Twine ¤t_directory, SmallVectorImpl< char > &path)
Make path an absolute path.
std::error_code create_directory(const Twine &path, bool IgnoreExisting=true, perms Perms=owner_all|group_all)
Create the directory in path.
@ CD_CreateAlways
CD_CreateAlways - When opening a file:
bool has_filename(const Twine &path, Style style=Style::native)
Has filename?
bool has_relative_path(const Twine &path, Style style=Style::native)
Has relative path?
std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
StringRef - Represent a constant reference to a string, i.e.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
ErrorOr< perms > getPermissions(const Twine &Path)
Get file permissions.
add sub stmia L5 ldr r0 bl L_printf $stub Instead of a and a wouldn t it be better to do three moves *Return an aggregate type is even return S
std::error_code rename(const Twine &from, const Twine &to)
Rename from to to.
size_t find_last_of(char C, size_t From=npos) const
Find the last character in the string that is C, or npos if not found.
detail::scope_exit< std::decay_t< Callable > > make_scope_exit(Callable &&F)
constexpr size_t size() const
size - Get the string size.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
StringRef root_name(StringRef path, Style style=Style::native)
Get root name.
void swap(SmallVectorImpl &RHS)
value_type read(const void *memory, endianness endian)
Read a value of a particular endianness from memory.
Lightweight error class with error context and mandatory checking.
std::string getMainExecutable(const char *argv0, void *MainExecAddr)
Return the path to the main executable, given the value of argv[0] from program startup and the addre...
bool has_root_name(const Twine &path, Style style=Style::native)
Has root name?
void system_temp_directory(bool erasedOnReboot, SmallVectorImpl< char > &result)
Get the typical temporary directory for the system, e.g., "/var/tmp" or "C:/TEMP".
bool is_directory(const basic_file_status &status)
Does status represent a directory?
StringRef str() const
Explicit conversion to StringRef.
bool has_root_path(const Twine &path, Style style=Style::native)
Has root path?
void DontRemoveFileOnSignal(StringRef Filename)
This function removes a file from the list of files to be removed on signal delivery.
Error takeError()
Take ownership of the stored error.
bool is_absolute_gnu(const Twine &path, Style style=Style::native)
Is path absolute using GNU rules?
bool is_separator(char value, Style style=Style::native)
Check whether the given char is a path separator on the host OS.
ErrorOr< MD5::MD5Result > md5_contents(int FD)
Compute an MD5 hash of a file's contents.
std::optional< std::vector< StOtherPiece > > Other
StringRef filename(StringRef path, Style style=Style::native)
Get filename.
constexpr bool is_style_windows(Style S)
Check if S uses Windows path rules.
std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
Represents either an error or a value T.
void remove_filename(SmallVectorImpl< char > &path, Style style=Style::native)
Remove the last component from path unless it is the root dir.
reverse_iterator rend(StringRef path)
Get reverse end iterator over path.
@ operation_not_permitted
ArrayRef(const T &OneElt) -> ArrayRef< T >
std::string str() const
str - Get the contents as an std::string.
bool has_root_directory(const Twine &path, Style style=Style::native)
Has root directory?
StringRef toStringRef(SmallVectorImpl< char > &Out) const
This returns the twine as a single StringRef if it can be represented as such.
@ CD_CreateNew
CD_CreateNew - When opening a file:
static std::error_code createUniqueEntity(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, bool MakeAbsolute, FSEntity Type, sys::fs::OpenFlags Flags=sys::fs::OF_None, unsigned Mode=0)
Represents a temporary file.
BlockVerifier::State From
StringRef relative_path(StringRef path, Style style=Style::native)
Get relative path.
static std::error_code copy_file_internal(int ReadFD, int WriteFD)
the resulting code requires compare and branches when and if the revised code is with conditional branches instead of More there is a byte word extend before each where there should be only and the condition codes are not remembered when the same two values are compared twice More LSR enhancements i8 and i32 load store addressing modes are identical int int int d
bool RemoveFileOnSignal(StringRef Filename, std::string *ErrMsg=nullptr)
This function registers signal handlers to ensure that if a signal gets delivered that the named file...
bool is_relative(const Twine &path, Style style=Style::native)
Is path relative?
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
std::error_code copy_file(const Twine &From, const Twine &To)
Copy the contents of From to To.
StringRef stem(StringRef path, Style style=Style::native)
Get stem.
std::error_code openFileForRead(const Twine &Name, int &ResultFD, OpenFlags Flags=OF_None, SmallVectorImpl< char > *RealPath=nullptr)
Opens the file with the given name in a read-only mode, returning its open file descriptor.