FileSystem.h revision 236385
1//===- llvm/Support/FileSystem.h - File System OS Concept -------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file declares the llvm::sys::fs namespace. It is designed after 11// TR2/boost filesystem (v3), but modified to remove exception handling and the 12// path class. 13// 14// All functions return an error_code and their actual work via the last out 15// argument. The out argument is defined if and only if errc::success is 16// returned. A function may return any error code in the generic or system 17// category. However, they shall be equivalent to any error conditions listed 18// in each functions respective documentation if the condition applies. [ note: 19// this does not guarantee that error_code will be in the set of explicitly 20// listed codes, but it does guarantee that if any of the explicitly listed 21// errors occur, the correct error_code will be used ]. All functions may 22// return errc::not_enough_memory if there is not enough memory to complete the 23// operation. 24// 25//===----------------------------------------------------------------------===// 26 27#ifndef LLVM_SUPPORT_FILE_SYSTEM_H 28#define LLVM_SUPPORT_FILE_SYSTEM_H 29 30#include "llvm/ADT/IntrusiveRefCntPtr.h" 31#include "llvm/ADT/SmallString.h" 32#include "llvm/ADT/Twine.h" 33#include "llvm/Support/DataTypes.h" 34#include "llvm/Support/ErrorHandling.h" 35#include "llvm/Support/system_error.h" 36#include <ctime> 37#include <iterator> 38#include <stack> 39#include <string> 40#include <vector> 41 42#if HAVE_SYS_STAT_H 43#include <sys/stat.h> 44#endif 45 46namespace llvm { 47namespace sys { 48namespace fs { 49 50/// file_type - An "enum class" enumeration for the file system's view of the 51/// type. 52struct file_type { 53 enum _ { 54 status_error, 55 file_not_found, 56 regular_file, 57 directory_file, 58 symlink_file, 59 block_file, 60 character_file, 61 fifo_file, 62 socket_file, 63 type_unknown 64 }; 65 66 file_type(_ v) : v_(v) {} 67 explicit file_type(int v) : v_(_(v)) {} 68 operator int() const {return v_;} 69 70private: 71 int v_; 72}; 73 74/// copy_option - An "enum class" enumeration of copy semantics for copy 75/// operations. 76struct copy_option { 77 enum _ { 78 fail_if_exists, 79 overwrite_if_exists 80 }; 81 82 copy_option(_ v) : v_(v) {} 83 explicit copy_option(int v) : v_(_(v)) {} 84 operator int() const {return v_;} 85 86private: 87 int v_; 88}; 89 90/// space_info - Self explanatory. 91struct space_info { 92 uint64_t capacity; 93 uint64_t free; 94 uint64_t available; 95}; 96 97/// file_status - Represents the result of a call to stat and friends. It has 98/// a platform specific member to store the result. 99class file_status 100{ 101 #if defined(LLVM_ON_UNIX) 102 dev_t st_dev; 103 ino_t st_ino; 104 #elif defined (LLVM_ON_WIN32) 105 uint32_t LastWriteTimeHigh; 106 uint32_t LastWriteTimeLow; 107 uint32_t VolumeSerialNumber; 108 uint32_t FileSizeHigh; 109 uint32_t FileSizeLow; 110 uint32_t FileIndexHigh; 111 uint32_t FileIndexLow; 112 #endif 113 friend bool equivalent(file_status A, file_status B); 114 friend error_code status(const Twine &path, file_status &result); 115 file_type Type; 116public: 117 explicit file_status(file_type v=file_type::status_error) 118 : Type(v) {} 119 120 file_type type() const { return Type; } 121 void type(file_type v) { Type = v; } 122}; 123 124/// file_magic - An "enum class" enumeration of file types based on magic (the first 125/// N bytes of the file). 126struct file_magic { 127 enum _ { 128 unknown = 0, ///< Unrecognized file 129 bitcode, ///< Bitcode file 130 archive, ///< ar style archive file 131 elf_relocatable, ///< ELF Relocatable object file 132 elf_executable, ///< ELF Executable image 133 elf_shared_object, ///< ELF dynamically linked shared lib 134 elf_core, ///< ELF core image 135 macho_object, ///< Mach-O Object file 136 macho_executable, ///< Mach-O Executable 137 macho_fixed_virtual_memory_shared_lib, ///< Mach-O Shared Lib, FVM 138 macho_core, ///< Mach-O Core File 139 macho_preload_executabl, ///< Mach-O Preloaded Executable 140 macho_dynamically_linked_shared_lib, ///< Mach-O dynlinked shared lib 141 macho_dynamic_linker, ///< The Mach-O dynamic linker 142 macho_bundle, ///< Mach-O Bundle file 143 macho_dynamically_linked_shared_lib_stub, ///< Mach-O Shared lib stub 144 macho_dsym_companion, ///< Mach-O dSYM companion file 145 coff_object, ///< COFF object file 146 pecoff_executable ///< PECOFF executable file 147 }; 148 149 bool is_object() const { 150 return v_ == unknown ? false : true; 151 } 152 153 file_magic() : v_(unknown) {} 154 file_magic(_ v) : v_(v) {} 155 explicit file_magic(int v) : v_(_(v)) {} 156 operator int() const {return v_;} 157 158private: 159 int v_; 160}; 161 162/// @} 163/// @name Physical Operators 164/// @{ 165 166/// @brief Make \a path an absolute path. 167/// 168/// Makes \a path absolute using the current directory if it is not already. An 169/// empty \a path will result in the current directory. 170/// 171/// /absolute/path => /absolute/path 172/// relative/../path => <current-directory>/relative/../path 173/// 174/// @param path A path that is modified to be an absolute path. 175/// @returns errc::success if \a path has been made absolute, otherwise a 176/// platform specific error_code. 177error_code make_absolute(SmallVectorImpl<char> &path); 178 179/// @brief Copy the file at \a from to the path \a to. 180/// 181/// @param from The path to copy the file from. 182/// @param to The path to copy the file to. 183/// @param copt Behavior if \a to already exists. 184/// @returns errc::success if the file has been successfully copied. 185/// errc::file_exists if \a to already exists and \a copt == 186/// copy_option::fail_if_exists. Otherwise a platform specific 187/// error_code. 188error_code copy_file(const Twine &from, const Twine &to, 189 copy_option copt = copy_option::fail_if_exists); 190 191/// @brief Create all the non-existent directories in path. 192/// 193/// @param path Directories to create. 194/// @param existed Set to true if \a path already existed, false otherwise. 195/// @returns errc::success if is_directory(path) and existed have been set, 196/// otherwise a platform specific error_code. 197error_code create_directories(const Twine &path, bool &existed); 198 199/// @brief Create the directory in path. 200/// 201/// @param path Directory to create. 202/// @param existed Set to true if \a path already existed, false otherwise. 203/// @returns errc::success if is_directory(path) and existed have been set, 204/// otherwise a platform specific error_code. 205error_code create_directory(const Twine &path, bool &existed); 206 207/// @brief Create a hard link from \a from to \a to. 208/// 209/// @param to The path to hard link to. 210/// @param from The path to hard link from. This is created. 211/// @returns errc::success if exists(to) && exists(from) && equivalent(to, from) 212/// , otherwise a platform specific error_code. 213error_code create_hard_link(const Twine &to, const Twine &from); 214 215/// @brief Create a symbolic link from \a from to \a to. 216/// 217/// @param to The path to symbolically link to. 218/// @param from The path to symbolically link from. This is created. 219/// @returns errc::success if exists(to) && exists(from) && is_symlink(from), 220/// otherwise a platform specific error_code. 221error_code create_symlink(const Twine &to, const Twine &from); 222 223/// @brief Get the current path. 224/// 225/// @param result Holds the current path on return. 226/// @results errc::success if the current path has been stored in result, 227/// otherwise a platform specific error_code. 228error_code current_path(SmallVectorImpl<char> &result); 229 230/// @brief Remove path. Equivalent to POSIX remove(). 231/// 232/// @param path Input path. 233/// @param existed Set to true if \a path existed, false if it did not. 234/// undefined otherwise. 235/// @results errc::success if path has been removed and existed has been 236/// successfully set, otherwise a platform specific error_code. 237error_code remove(const Twine &path, bool &existed); 238 239/// @brief Recursively remove all files below \a path, then \a path. Files are 240/// removed as if by POSIX remove(). 241/// 242/// @param path Input path. 243/// @param num_removed Number of files removed. 244/// @results errc::success if path has been removed and num_removed has been 245/// successfully set, otherwise a platform specific error_code. 246error_code remove_all(const Twine &path, uint32_t &num_removed); 247 248/// @brief Rename \a from to \a to. Files are renamed as if by POSIX rename(). 249/// 250/// @param from The path to rename from. 251/// @param to The path to rename to. This is created. 252error_code rename(const Twine &from, const Twine &to); 253 254/// @brief Resize path to size. File is resized as if by POSIX truncate(). 255/// 256/// @param path Input path. 257/// @param size Size to resize to. 258/// @returns errc::success if \a path has been resized to \a size, otherwise a 259/// platform specific error_code. 260error_code resize_file(const Twine &path, uint64_t size); 261 262/// @} 263/// @name Physical Observers 264/// @{ 265 266/// @brief Does file exist? 267/// 268/// @param status A file_status previously returned from stat. 269/// @results True if the file represented by status exists, false if it does 270/// not. 271bool exists(file_status status); 272 273/// @brief Does file exist? 274/// 275/// @param path Input path. 276/// @param result Set to true if the file represented by status exists, false if 277/// it does not. Undefined otherwise. 278/// @results errc::success if result has been successfully set, otherwise a 279/// platform specific error_code. 280error_code exists(const Twine &path, bool &result); 281 282/// @brief Simpler version of exists for clients that don't need to 283/// differentiate between an error and false. 284inline bool exists(const Twine &path) { 285 bool result; 286 return !exists(path, result) && result; 287} 288 289/// @brief Do file_status's represent the same thing? 290/// 291/// @param A Input file_status. 292/// @param B Input file_status. 293/// 294/// assert(status_known(A) || status_known(B)); 295/// 296/// @results True if A and B both represent the same file system entity, false 297/// otherwise. 298bool equivalent(file_status A, file_status B); 299 300/// @brief Do paths represent the same thing? 301/// 302/// assert(status_known(A) || status_known(B)); 303/// 304/// @param A Input path A. 305/// @param B Input path B. 306/// @param result Set to true if stat(A) and stat(B) have the same device and 307/// inode (or equivalent). 308/// @results errc::success if result has been successfully set, otherwise a 309/// platform specific error_code. 310error_code equivalent(const Twine &A, const Twine &B, bool &result); 311 312/// @brief Get file size. 313/// 314/// @param path Input path. 315/// @param result Set to the size of the file in \a path. 316/// @returns errc::success if result has been successfully set, otherwise a 317/// platform specific error_code. 318error_code file_size(const Twine &path, uint64_t &result); 319 320/// @brief Does status represent a directory? 321/// 322/// @param status A file_status previously returned from status. 323/// @results status.type() == file_type::directory_file. 324bool is_directory(file_status status); 325 326/// @brief Is path a directory? 327/// 328/// @param path Input path. 329/// @param result Set to true if \a path is a directory, false if it is not. 330/// Undefined otherwise. 331/// @results errc::success if result has been successfully set, otherwise a 332/// platform specific error_code. 333error_code is_directory(const Twine &path, bool &result); 334 335/// @brief Does status represent a regular file? 336/// 337/// @param status A file_status previously returned from status. 338/// @results status_known(status) && status.type() == file_type::regular_file. 339bool is_regular_file(file_status status); 340 341/// @brief Is path a regular file? 342/// 343/// @param path Input path. 344/// @param result Set to true if \a path is a regular file, false if it is not. 345/// Undefined otherwise. 346/// @results errc::success if result has been successfully set, otherwise a 347/// platform specific error_code. 348error_code is_regular_file(const Twine &path, bool &result); 349 350/// @brief Does this status represent something that exists but is not a 351/// directory, regular file, or symlink? 352/// 353/// @param status A file_status previously returned from status. 354/// @results exists(s) && !is_regular_file(s) && !is_directory(s) && 355/// !is_symlink(s) 356bool is_other(file_status status); 357 358/// @brief Is path something that exists but is not a directory, 359/// regular file, or symlink? 360/// 361/// @param path Input path. 362/// @param result Set to true if \a path exists, but is not a directory, regular 363/// file, or a symlink, false if it does not. Undefined otherwise. 364/// @results errc::success if result has been successfully set, otherwise a 365/// platform specific error_code. 366error_code is_other(const Twine &path, bool &result); 367 368/// @brief Does status represent a symlink? 369/// 370/// @param status A file_status previously returned from stat. 371/// @param result status.type() == symlink_file. 372bool is_symlink(file_status status); 373 374/// @brief Is path a symlink? 375/// 376/// @param path Input path. 377/// @param result Set to true if \a path is a symlink, false if it is not. 378/// Undefined otherwise. 379/// @results errc::success if result has been successfully set, otherwise a 380/// platform specific error_code. 381error_code is_symlink(const Twine &path, bool &result); 382 383/// @brief Get file status as if by POSIX stat(). 384/// 385/// @param path Input path. 386/// @param result Set to the file status. 387/// @results errc::success if result has been successfully set, otherwise a 388/// platform specific error_code. 389error_code status(const Twine &path, file_status &result); 390 391/// @brief Is status available? 392/// 393/// @param path Input path. 394/// @results True if status() != status_error. 395bool status_known(file_status s); 396 397/// @brief Is status available? 398/// 399/// @param path Input path. 400/// @param result Set to true if status() != status_error. 401/// @results errc::success if result has been successfully set, otherwise a 402/// platform specific error_code. 403error_code status_known(const Twine &path, bool &result); 404 405/// @brief Generate a unique path and open it as a file. 406/// 407/// Generates a unique path suitable for a temporary file and then opens it as a 408/// file. The name is based on \a model with '%' replaced by a random char in 409/// [0-9a-f]. If \a model is not an absolute path, a suitable temporary 410/// directory will be prepended. 411/// 412/// This is an atomic operation. Either the file is created and opened, or the 413/// file system is left untouched. 414/// 415/// clang-%%-%%-%%-%%-%%.s => /tmp/clang-a0-b1-c2-d3-e4.s 416/// 417/// @param model Name to base unique path off of. 418/// @param result_fs Set to the opened file's file descriptor. 419/// @param result_path Set to the opened file's absolute path. 420/// @param makeAbsolute If true and @model is not an absolute path, a temp 421/// directory will be prepended. 422/// @results errc::success if result_{fd,path} have been successfully set, 423/// otherwise a platform specific error_code. 424error_code unique_file(const Twine &model, int &result_fd, 425 SmallVectorImpl<char> &result_path, 426 bool makeAbsolute = true, unsigned mode = 0600); 427 428/// @brief Canonicalize path. 429/// 430/// Sets result to the file system's idea of what path is. The result is always 431/// absolute and has the same capitalization as the file system. 432/// 433/// @param path Input path. 434/// @param result Set to the canonicalized version of \a path. 435/// @results errc::success if result has been successfully set, otherwise a 436/// platform specific error_code. 437error_code canonicalize(const Twine &path, SmallVectorImpl<char> &result); 438 439/// @brief Are \a path's first bytes \a magic? 440/// 441/// @param path Input path. 442/// @param magic Byte sequence to compare \a path's first len(magic) bytes to. 443/// @results errc::success if result has been successfully set, otherwise a 444/// platform specific error_code. 445error_code has_magic(const Twine &path, const Twine &magic, bool &result); 446 447/// @brief Get \a path's first \a len bytes. 448/// 449/// @param path Input path. 450/// @param len Number of magic bytes to get. 451/// @param result Set to the first \a len bytes in the file pointed to by 452/// \a path. Or the entire file if file_size(path) < len, in which 453/// case result.size() returns the size of the file. 454/// @results errc::success if result has been successfully set, 455/// errc::value_too_large if len is larger then the file pointed to by 456/// \a path, otherwise a platform specific error_code. 457error_code get_magic(const Twine &path, uint32_t len, 458 SmallVectorImpl<char> &result); 459 460/// @brief Identify the type of a binary file based on how magical it is. 461file_magic identify_magic(StringRef magic); 462 463/// @brief Get and identify \a path's type based on its content. 464/// 465/// @param path Input path. 466/// @param result Set to the type of file, or LLVMFileType::Unknown_FileType. 467/// @results errc::success if result has been successfully set, otherwise a 468/// platform specific error_code. 469error_code identify_magic(const Twine &path, file_magic &result); 470 471/// @brief Get library paths the system linker uses. 472/// 473/// @param result Set to the list of system library paths. 474/// @results errc::success if result has been successfully set, otherwise a 475/// platform specific error_code. 476error_code GetSystemLibraryPaths(SmallVectorImpl<std::string> &result); 477 478/// @brief Get bitcode library paths the system linker uses 479/// + LLVM_LIB_SEARCH_PATH + LLVM_LIBDIR. 480/// 481/// @param result Set to the list of bitcode library paths. 482/// @results errc::success if result has been successfully set, otherwise a 483/// platform specific error_code. 484error_code GetBitcodeLibraryPaths(SmallVectorImpl<std::string> &result); 485 486/// @brief Find a library. 487/// 488/// Find the path to a library using its short name. Use the system 489/// dependent library paths to locate the library. 490/// 491/// c => /usr/lib/libc.so 492/// 493/// @param short_name Library name one would give to the system linker. 494/// @param result Set to the absolute path \a short_name represents. 495/// @results errc::success if result has been successfully set, otherwise a 496/// platform specific error_code. 497error_code FindLibrary(const Twine &short_name, SmallVectorImpl<char> &result); 498 499/// @brief Get absolute path of main executable. 500/// 501/// @param argv0 The program name as it was spelled on the command line. 502/// @param MainAddr Address of some symbol in the executable (not in a library). 503/// @param result Set to the absolute path of the current executable. 504/// @results errc::success if result has been successfully set, otherwise a 505/// platform specific error_code. 506error_code GetMainExecutable(const char *argv0, void *MainAddr, 507 SmallVectorImpl<char> &result); 508 509/// @} 510/// @name Iterators 511/// @{ 512 513/// directory_entry - A single entry in a directory. Caches the status either 514/// from the result of the iteration syscall, or the first time status is 515/// called. 516class directory_entry { 517 std::string Path; 518 mutable file_status Status; 519 520public: 521 explicit directory_entry(const Twine &path, file_status st = file_status()) 522 : Path(path.str()) 523 , Status(st) {} 524 525 directory_entry() {} 526 527 void assign(const Twine &path, file_status st = file_status()) { 528 Path = path.str(); 529 Status = st; 530 } 531 532 void replace_filename(const Twine &filename, file_status st = file_status()); 533 534 const std::string &path() const { return Path; } 535 error_code status(file_status &result) const; 536 537 bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; } 538 bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); } 539 bool operator< (const directory_entry& rhs) const; 540 bool operator<=(const directory_entry& rhs) const; 541 bool operator> (const directory_entry& rhs) const; 542 bool operator>=(const directory_entry& rhs) const; 543}; 544 545namespace detail { 546 struct DirIterState; 547 548 error_code directory_iterator_construct(DirIterState&, StringRef); 549 error_code directory_iterator_increment(DirIterState&); 550 error_code directory_iterator_destruct(DirIterState&); 551 552 /// DirIterState - Keeps state for the directory_iterator. It is reference 553 /// counted in order to preserve InputIterator semantics on copy. 554 struct DirIterState : public RefCountedBase<DirIterState> { 555 DirIterState() 556 : IterationHandle(0) {} 557 558 ~DirIterState() { 559 directory_iterator_destruct(*this); 560 } 561 562 intptr_t IterationHandle; 563 directory_entry CurrentEntry; 564 }; 565} 566 567/// directory_iterator - Iterates through the entries in path. There is no 568/// operator++ because we need an error_code. If it's really needed we can make 569/// it call report_fatal_error on error. 570class directory_iterator { 571 IntrusiveRefCntPtr<detail::DirIterState> State; 572 573public: 574 explicit directory_iterator(const Twine &path, error_code &ec) { 575 State = new detail::DirIterState; 576 SmallString<128> path_storage; 577 ec = detail::directory_iterator_construct(*State, 578 path.toStringRef(path_storage)); 579 } 580 581 explicit directory_iterator(const directory_entry &de, error_code &ec) { 582 State = new detail::DirIterState; 583 ec = detail::directory_iterator_construct(*State, de.path()); 584 } 585 586 /// Construct end iterator. 587 directory_iterator() : State(new detail::DirIterState) {} 588 589 // No operator++ because we need error_code. 590 directory_iterator &increment(error_code &ec) { 591 ec = directory_iterator_increment(*State); 592 return *this; 593 } 594 595 const directory_entry &operator*() const { return State->CurrentEntry; } 596 const directory_entry *operator->() const { return &State->CurrentEntry; } 597 598 bool operator==(const directory_iterator &RHS) const { 599 return State->CurrentEntry == RHS.State->CurrentEntry; 600 } 601 602 bool operator!=(const directory_iterator &RHS) const { 603 return !(*this == RHS); 604 } 605 // Other members as required by 606 // C++ Std, 24.1.1 Input iterators [input.iterators] 607}; 608 609namespace detail { 610 /// RecDirIterState - Keeps state for the recursive_directory_iterator. It is 611 /// reference counted in order to preserve InputIterator semantics on copy. 612 struct RecDirIterState : public RefCountedBase<RecDirIterState> { 613 RecDirIterState() 614 : Level(0) 615 , HasNoPushRequest(false) {} 616 617 std::stack<directory_iterator, std::vector<directory_iterator> > Stack; 618 uint16_t Level; 619 bool HasNoPushRequest; 620 }; 621} 622 623/// recursive_directory_iterator - Same as directory_iterator except for it 624/// recurses down into child directories. 625class recursive_directory_iterator { 626 IntrusiveRefCntPtr<detail::RecDirIterState> State; 627 628public: 629 recursive_directory_iterator() {} 630 explicit recursive_directory_iterator(const Twine &path, error_code &ec) 631 : State(new detail::RecDirIterState) { 632 State->Stack.push(directory_iterator(path, ec)); 633 if (State->Stack.top() == directory_iterator()) 634 State.reset(); 635 } 636 // No operator++ because we need error_code. 637 recursive_directory_iterator &increment(error_code &ec) { 638 static const directory_iterator end_itr; 639 640 if (State->HasNoPushRequest) 641 State->HasNoPushRequest = false; 642 else { 643 file_status st; 644 if ((ec = State->Stack.top()->status(st))) return *this; 645 if (is_directory(st)) { 646 State->Stack.push(directory_iterator(*State->Stack.top(), ec)); 647 if (ec) return *this; 648 if (State->Stack.top() != end_itr) { 649 ++State->Level; 650 return *this; 651 } 652 State->Stack.pop(); 653 } 654 } 655 656 while (!State->Stack.empty() 657 && State->Stack.top().increment(ec) == end_itr) { 658 State->Stack.pop(); 659 --State->Level; 660 } 661 662 // Check if we are done. If so, create an end iterator. 663 if (State->Stack.empty()) 664 State.reset(); 665 666 return *this; 667 } 668 669 const directory_entry &operator*() const { return *State->Stack.top(); } 670 const directory_entry *operator->() const { return &*State->Stack.top(); } 671 672 // observers 673 /// Gets the current level. Starting path is at level 0. 674 int level() const { return State->Level; } 675 676 /// Returns true if no_push has been called for this directory_entry. 677 bool no_push_request() const { return State->HasNoPushRequest; } 678 679 // modifiers 680 /// Goes up one level if Level > 0. 681 void pop() { 682 assert(State && "Cannot pop and end itertor!"); 683 assert(State->Level > 0 && "Cannot pop an iterator with level < 1"); 684 685 static const directory_iterator end_itr; 686 error_code ec; 687 do { 688 if (ec) 689 report_fatal_error("Error incrementing directory iterator."); 690 State->Stack.pop(); 691 --State->Level; 692 } while (!State->Stack.empty() 693 && State->Stack.top().increment(ec) == end_itr); 694 695 // Check if we are done. If so, create an end iterator. 696 if (State->Stack.empty()) 697 State.reset(); 698 } 699 700 /// Does not go down into the current directory_entry. 701 void no_push() { State->HasNoPushRequest = true; } 702 703 bool operator==(const recursive_directory_iterator &RHS) const { 704 return State == RHS.State; 705 } 706 707 bool operator!=(const recursive_directory_iterator &RHS) const { 708 return !(*this == RHS); 709 } 710 // Other members as required by 711 // C++ Std, 24.1.1 Input iterators [input.iterators] 712}; 713 714/// @} 715 716} // end namespace fs 717} // end namespace sys 718} // end namespace llvm 719 720#endif 721