123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- //===- Support/FileUtilities.cpp - File System Utilities ------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file implements a family of utility functions which are useful for doing
- // various things with files.
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/Support/FileUtilities.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Support/Error.h"
- #include "llvm/Support/ErrorOr.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/Process.h"
- #include "llvm/Support/raw_ostream.h"
- #include <cmath>
- #include <cstdint>
- #include <cstdlib>
- #include <cstring>
- #include <memory>
- #include <system_error>
- using namespace llvm;
- static bool isSignedChar(char C) {
- return (C == '+' || C == '-');
- }
- static bool isExponentChar(char C) {
- switch (C) {
- case 'D': // Strange exponential notation.
- case 'd': // Strange exponential notation.
- case 'e':
- case 'E': return true;
- default: return false;
- }
- }
- static bool isNumberChar(char C) {
- switch (C) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case '.': return true;
- default: return isSignedChar(C) || isExponentChar(C);
- }
- }
- static const char *BackupNumber(const char *Pos, const char *FirstChar) {
- // If we didn't stop in the middle of a number, don't backup.
- if (!isNumberChar(*Pos)) return Pos;
- // Otherwise, return to the start of the number.
- bool HasPeriod = false;
- while (Pos > FirstChar && isNumberChar(Pos[-1])) {
- // Backup over at most one period.
- if (Pos[-1] == '.') {
- if (HasPeriod)
- break;
- HasPeriod = true;
- }
- --Pos;
- if (Pos > FirstChar && isSignedChar(Pos[0]) && !isExponentChar(Pos[-1]))
- break;
- }
- return Pos;
- }
- /// EndOfNumber - Return the first character that is not part of the specified
- /// number. This assumes that the buffer is null terminated, so it won't fall
- /// off the end.
- static const char *EndOfNumber(const char *Pos) {
- while (isNumberChar(*Pos))
- ++Pos;
- return Pos;
- }
- /// CompareNumbers - compare two numbers, returning true if they are different.
- static bool CompareNumbers(const char *&F1P, const char *&F2P,
- const char *F1End, const char *F2End,
- double AbsTolerance, double RelTolerance,
- std::string *ErrorMsg) {
- const char *F1NumEnd, *F2NumEnd;
- double V1 = 0.0, V2 = 0.0;
- // If one of the positions is at a space and the other isn't, chomp up 'til
- // the end of the space.
- while (isSpace(static_cast<unsigned char>(*F1P)) && F1P != F1End)
- ++F1P;
- while (isSpace(static_cast<unsigned char>(*F2P)) && F2P != F2End)
- ++F2P;
- // If we stop on numbers, compare their difference.
- if (!isNumberChar(*F1P) || !isNumberChar(*F2P)) {
- // The diff failed.
- F1NumEnd = F1P;
- F2NumEnd = F2P;
- } else {
- // Note that some ugliness is built into this to permit support for numbers
- // that use "D" or "d" as their exponential marker, e.g. "1.234D45". This
- // occurs in 200.sixtrack in spec2k.
- V1 = strtod(F1P, const_cast<char**>(&F1NumEnd));
- V2 = strtod(F2P, const_cast<char**>(&F2NumEnd));
- if (*F1NumEnd == 'D' || *F1NumEnd == 'd') {
- // Copy string into tmp buffer to replace the 'D' with an 'e'.
- SmallString<200> StrTmp(F1P, EndOfNumber(F1NumEnd)+1);
- // Strange exponential notation!
- StrTmp[static_cast<unsigned>(F1NumEnd-F1P)] = 'e';
- V1 = strtod(&StrTmp[0], const_cast<char**>(&F1NumEnd));
- F1NumEnd = F1P + (F1NumEnd-&StrTmp[0]);
- }
- if (*F2NumEnd == 'D' || *F2NumEnd == 'd') {
- // Copy string into tmp buffer to replace the 'D' with an 'e'.
- SmallString<200> StrTmp(F2P, EndOfNumber(F2NumEnd)+1);
- // Strange exponential notation!
- StrTmp[static_cast<unsigned>(F2NumEnd-F2P)] = 'e';
- V2 = strtod(&StrTmp[0], const_cast<char**>(&F2NumEnd));
- F2NumEnd = F2P + (F2NumEnd-&StrTmp[0]);
- }
- }
- if (F1NumEnd == F1P || F2NumEnd == F2P) {
- if (ErrorMsg) {
- *ErrorMsg = "FP Comparison failed, not a numeric difference between '";
- *ErrorMsg += F1P[0];
- *ErrorMsg += "' and '";
- *ErrorMsg += F2P[0];
- *ErrorMsg += "'";
- }
- return true;
- }
- // Check to see if these are inside the absolute tolerance
- if (AbsTolerance < std::abs(V1-V2)) {
- // Nope, check the relative tolerance...
- double Diff;
- if (V2)
- Diff = std::abs(V1/V2 - 1.0);
- else if (V1)
- Diff = std::abs(V2/V1 - 1.0);
- else
- Diff = 0; // Both zero.
- if (Diff > RelTolerance) {
- if (ErrorMsg) {
- raw_string_ostream(*ErrorMsg)
- << "Compared: " << V1 << " and " << V2 << '\n'
- << "abs. diff = " << std::abs(V1-V2) << " rel.diff = " << Diff << '\n'
- << "Out of tolerance: rel/abs: " << RelTolerance << '/'
- << AbsTolerance;
- }
- return true;
- }
- }
- // Otherwise, advance our read pointers to the end of the numbers.
- F1P = F1NumEnd; F2P = F2NumEnd;
- return false;
- }
- /// DiffFilesWithTolerance - Compare the two files specified, returning 0 if the
- /// files match, 1 if they are different, and 2 if there is a file error. This
- /// function differs from DiffFiles in that you can specify an absolete and
- /// relative FP error that is allowed to exist. If you specify a string to fill
- /// in for the error option, it will set the string to an error message if an
- /// error occurs, allowing the caller to distinguish between a failed diff and a
- /// file system error.
- ///
- int llvm::DiffFilesWithTolerance(StringRef NameA,
- StringRef NameB,
- double AbsTol, double RelTol,
- std::string *Error) {
- // Now its safe to mmap the files into memory because both files
- // have a non-zero size.
- ErrorOr<std::unique_ptr<MemoryBuffer>> F1OrErr = MemoryBuffer::getFile(NameA);
- if (std::error_code EC = F1OrErr.getError()) {
- if (Error)
- *Error = EC.message();
- return 2;
- }
- MemoryBuffer &F1 = *F1OrErr.get();
- ErrorOr<std::unique_ptr<MemoryBuffer>> F2OrErr = MemoryBuffer::getFile(NameB);
- if (std::error_code EC = F2OrErr.getError()) {
- if (Error)
- *Error = EC.message();
- return 2;
- }
- MemoryBuffer &F2 = *F2OrErr.get();
- // Okay, now that we opened the files, scan them for the first difference.
- const char *File1Start = F1.getBufferStart();
- const char *File2Start = F2.getBufferStart();
- const char *File1End = F1.getBufferEnd();
- const char *File2End = F2.getBufferEnd();
- const char *F1P = File1Start;
- const char *F2P = File2Start;
- uint64_t A_size = F1.getBufferSize();
- uint64_t B_size = F2.getBufferSize();
- // Are the buffers identical? Common case: Handle this efficiently.
- if (A_size == B_size &&
- std::memcmp(File1Start, File2Start, A_size) == 0)
- return 0;
- // Otherwise, we are done a tolerances are set.
- if (AbsTol == 0 && RelTol == 0) {
- if (Error)
- *Error = "Files differ without tolerance allowance";
- return 1; // Files different!
- }
- bool CompareFailed = false;
- while (true) {
- // Scan for the end of file or next difference.
- while (F1P < File1End && F2P < File2End && *F1P == *F2P) {
- ++F1P;
- ++F2P;
- }
- if (F1P >= File1End || F2P >= File2End) break;
- // Okay, we must have found a difference. Backup to the start of the
- // current number each stream is at so that we can compare from the
- // beginning.
- F1P = BackupNumber(F1P, File1Start);
- F2P = BackupNumber(F2P, File2Start);
- // Now that we are at the start of the numbers, compare them, exiting if
- // they don't match.
- if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error)) {
- CompareFailed = true;
- break;
- }
- }
- // Okay, we reached the end of file. If both files are at the end, we
- // succeeded.
- bool F1AtEnd = F1P >= File1End;
- bool F2AtEnd = F2P >= File2End;
- if (!CompareFailed && (!F1AtEnd || !F2AtEnd)) {
- // Else, we might have run off the end due to a number: backup and retry.
- if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
- if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
- F1P = BackupNumber(F1P, File1Start);
- F2P = BackupNumber(F2P, File2Start);
- // Now that we are at the start of the numbers, compare them, exiting if
- // they don't match.
- if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error))
- CompareFailed = true;
- // If we found the end, we succeeded.
- if (F1P < File1End || F2P < File2End)
- CompareFailed = true;
- }
- return CompareFailed;
- }
- void llvm::AtomicFileWriteError::log(raw_ostream &OS) const {
- OS << "atomic_write_error: ";
- switch (Error) {
- case atomic_write_error::failed_to_create_uniq_file:
- OS << "failed_to_create_uniq_file";
- return;
- case atomic_write_error::output_stream_error:
- OS << "output_stream_error";
- return;
- case atomic_write_error::failed_to_rename_temp_file:
- OS << "failed_to_rename_temp_file";
- return;
- }
- llvm_unreachable("unknown atomic_write_error value in "
- "failed_to_rename_temp_file::log()");
- }
- llvm::Error llvm::writeFileAtomically(StringRef TempPathModel,
- StringRef FinalPath, StringRef Buffer) {
- return writeFileAtomically(TempPathModel, FinalPath,
- [&Buffer](llvm::raw_ostream &OS) {
- OS.write(Buffer.data(), Buffer.size());
- return llvm::Error::success();
- });
- }
- llvm::Error llvm::writeFileAtomically(
- StringRef TempPathModel, StringRef FinalPath,
- std::function<llvm::Error(llvm::raw_ostream &)> Writer) {
- SmallString<128> GeneratedUniqPath;
- int TempFD;
- if (sys::fs::createUniqueFile(TempPathModel, TempFD, GeneratedUniqPath)) {
- return llvm::make_error<AtomicFileWriteError>(
- atomic_write_error::failed_to_create_uniq_file);
- }
- llvm::FileRemover RemoveTmpFileOnFail(GeneratedUniqPath);
- raw_fd_ostream OS(TempFD, /*shouldClose=*/true);
- if (llvm::Error Err = Writer(OS)) {
- return Err;
- }
- OS.close();
- if (OS.has_error()) {
- OS.clear_error();
- return llvm::make_error<AtomicFileWriteError>(
- atomic_write_error::output_stream_error);
- }
- if (sys::fs::rename(/*from=*/GeneratedUniqPath, /*to=*/FinalPath)) {
- return llvm::make_error<AtomicFileWriteError>(
- atomic_write_error::failed_to_rename_temp_file);
- }
- RemoveTmpFileOnFail.releaseFile();
- return Error::success();
- }
- Expected<FilePermissionsApplier>
- FilePermissionsApplier::create(StringRef InputFilename) {
- sys::fs::file_status Status;
- if (InputFilename != "-") {
- if (auto EC = sys::fs::status(InputFilename, Status))
- return createFileError(InputFilename, EC);
- } else {
- Status.permissions(static_cast<sys::fs::perms>(0777));
- }
- return FilePermissionsApplier(InputFilename, Status);
- }
- Error FilePermissionsApplier::apply(
- StringRef OutputFilename, bool CopyDates,
- std::optional<sys::fs::perms> OverwritePermissions) {
- sys::fs::file_status Status = InputStatus;
- if (OverwritePermissions)
- Status.permissions(*OverwritePermissions);
- int FD = 0;
- // Writing to stdout should not be treated as an error here, just
- // do not set access/modification times or permissions.
- if (OutputFilename == "-")
- return Error::success();
- if (std::error_code EC = sys::fs::openFileForWrite(OutputFilename, FD,
- sys::fs::CD_OpenExisting))
- return createFileError(OutputFilename, EC);
- if (CopyDates)
- if (std::error_code EC = sys::fs::setLastAccessAndModificationTime(
- FD, Status.getLastAccessedTime(), Status.getLastModificationTime()))
- return createFileError(OutputFilename, EC);
- sys::fs::file_status OStat;
- if (std::error_code EC = sys::fs::status(FD, OStat))
- return createFileError(OutputFilename, EC);
- if (OStat.type() == sys::fs::file_type::regular_file) {
- #ifndef _WIN32
- // Keep ownership if llvm-objcopy is called under root.
- if (OutputFilename == InputFilename && OStat.getUser() == 0)
- sys::fs::changeFileOwnership(FD, Status.getUser(), Status.getGroup());
- #endif
- sys::fs::perms Perm = Status.permissions();
- if (OutputFilename != InputFilename)
- Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000);
- #ifdef _WIN32
- if (std::error_code EC = sys::fs::setPermissions(OutputFilename, Perm))
- #else
- if (std::error_code EC = sys::fs::setPermissions(FD, Perm))
- #endif
- return createFileError(OutputFilename, EC);
- }
- if (std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD))
- return createFileError(OutputFilename, EC);
- return Error::success();
- }
- char llvm::AtomicFileWriteError::ID;
|