LLVM 20.0.0git
FileUtilities.cpp
Go to the documentation of this file.
1//===- Support/FileUtilities.cpp - File System Utilities ------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements a family of utility functions which are useful for doing
10// various things with files.
11//
12//===----------------------------------------------------------------------===//
13
17#include "llvm/Support/Error.h"
22#include <cmath>
23#include <cstdint>
24#include <cstdlib>
25#include <cstring>
26#include <memory>
27#include <system_error>
28
29using namespace llvm;
30
31static bool isSignedChar(char C) {
32 return (C == '+' || C == '-');
33}
34
35static bool isExponentChar(char C) {
36 switch (C) {
37 case 'D': // Strange exponential notation.
38 case 'd': // Strange exponential notation.
39 case 'e':
40 case 'E': return true;
41 default: return false;
42 }
43}
44
45static bool isNumberChar(char C) {
46 switch (C) {
47 case '0': case '1': case '2': case '3': case '4':
48 case '5': case '6': case '7': case '8': case '9':
49 case '.': return true;
50 default: return isSignedChar(C) || isExponentChar(C);
51 }
52}
53
54static const char *BackupNumber(const char *Pos, const char *FirstChar) {
55 // If we didn't stop in the middle of a number, don't backup.
56 if (!isNumberChar(*Pos)) return Pos;
57
58 // Otherwise, return to the start of the number.
59 bool HasPeriod = false;
60 while (Pos > FirstChar && isNumberChar(Pos[-1])) {
61 // Backup over at most one period.
62 if (Pos[-1] == '.') {
63 if (HasPeriod)
64 break;
65 HasPeriod = true;
66 }
67
68 --Pos;
69 if (Pos > FirstChar && isSignedChar(Pos[0]) && !isExponentChar(Pos[-1]))
70 break;
71 }
72 return Pos;
73}
74
75/// EndOfNumber - Return the first character that is not part of the specified
76/// number. This assumes that the buffer is null terminated, so it won't fall
77/// off the end.
78static const char *EndOfNumber(const char *Pos) {
79 while (isNumberChar(*Pos))
80 ++Pos;
81 return Pos;
82}
83
84/// CompareNumbers - compare two numbers, returning true if they are different.
85static bool CompareNumbers(const char *&F1P, const char *&F2P,
86 const char *F1End, const char *F2End,
87 double AbsTolerance, double RelTolerance,
88 std::string *ErrorMsg) {
89 const char *F1NumEnd, *F2NumEnd;
90 double V1 = 0.0, V2 = 0.0;
91
92 // If one of the positions is at a space and the other isn't, chomp up 'til
93 // the end of the space.
94 while (isSpace(static_cast<unsigned char>(*F1P)) && F1P != F1End)
95 ++F1P;
96 while (isSpace(static_cast<unsigned char>(*F2P)) && F2P != F2End)
97 ++F2P;
98
99 // If we stop on numbers, compare their difference.
100 if (!isNumberChar(*F1P) || !isNumberChar(*F2P)) {
101 // The diff failed.
102 F1NumEnd = F1P;
103 F2NumEnd = F2P;
104 } else {
105 // Note that some ugliness is built into this to permit support for numbers
106 // that use "D" or "d" as their exponential marker, e.g. "1.234D45". This
107 // occurs in 200.sixtrack in spec2k.
108 V1 = strtod(F1P, const_cast<char**>(&F1NumEnd));
109 V2 = strtod(F2P, const_cast<char**>(&F2NumEnd));
110
111 if (*F1NumEnd == 'D' || *F1NumEnd == 'd') {
112 // Copy string into tmp buffer to replace the 'D' with an 'e'.
113 SmallString<200> StrTmp(F1P, EndOfNumber(F1NumEnd)+1);
114 // Strange exponential notation!
115 StrTmp[static_cast<unsigned>(F1NumEnd-F1P)] = 'e';
116
117 V1 = strtod(&StrTmp[0], const_cast<char**>(&F1NumEnd));
118 F1NumEnd = F1P + (F1NumEnd-&StrTmp[0]);
119 }
120
121 if (*F2NumEnd == 'D' || *F2NumEnd == 'd') {
122 // Copy string into tmp buffer to replace the 'D' with an 'e'.
123 SmallString<200> StrTmp(F2P, EndOfNumber(F2NumEnd)+1);
124 // Strange exponential notation!
125 StrTmp[static_cast<unsigned>(F2NumEnd-F2P)] = 'e';
126
127 V2 = strtod(&StrTmp[0], const_cast<char**>(&F2NumEnd));
128 F2NumEnd = F2P + (F2NumEnd-&StrTmp[0]);
129 }
130 }
131
132 if (F1NumEnd == F1P || F2NumEnd == F2P) {
133 if (ErrorMsg) {
134 *ErrorMsg = "FP Comparison failed, not a numeric difference between '";
135 *ErrorMsg += F1P[0];
136 *ErrorMsg += "' and '";
137 *ErrorMsg += F2P[0];
138 *ErrorMsg += "'";
139 }
140 return true;
141 }
142
143 // Check to see if these are inside the absolute tolerance
144 if (AbsTolerance < std::abs(V1-V2)) {
145 // Nope, check the relative tolerance...
146 double Diff;
147 if (V2)
148 Diff = std::abs(V1/V2 - 1.0);
149 else if (V1)
150 Diff = std::abs(V2/V1 - 1.0);
151 else
152 Diff = 0; // Both zero.
153 if (Diff > RelTolerance) {
154 if (ErrorMsg) {
155 raw_string_ostream(*ErrorMsg)
156 << "Compared: " << V1 << " and " << V2 << '\n'
157 << "abs. diff = " << std::abs(V1-V2) << " rel.diff = " << Diff << '\n'
158 << "Out of tolerance: rel/abs: " << RelTolerance << '/'
159 << AbsTolerance;
160 }
161 return true;
162 }
163 }
164
165 // Otherwise, advance our read pointers to the end of the numbers.
166 F1P = F1NumEnd; F2P = F2NumEnd;
167 return false;
168}
169
170/// DiffFilesWithTolerance - Compare the two files specified, returning 0 if the
171/// files match, 1 if they are different, and 2 if there is a file error. This
172/// function differs from DiffFiles in that you can specify an absolute and
173/// relative FP error that is allowed to exist. If you specify a string to fill
174/// in for the error option, it will set the string to an error message if an
175/// error occurs, allowing the caller to distinguish between a failed diff and a
176/// file system error.
177///
179 StringRef NameB,
180 double AbsTol, double RelTol,
181 std::string *Error) {
182 // Now its safe to mmap the files into memory because both files
183 // have a non-zero size.
185 if (std::error_code EC = F1OrErr.getError()) {
186 if (Error)
187 *Error = EC.message();
188 return 2;
189 }
190 MemoryBuffer &F1 = *F1OrErr.get();
191
193 if (std::error_code EC = F2OrErr.getError()) {
194 if (Error)
195 *Error = EC.message();
196 return 2;
197 }
198 MemoryBuffer &F2 = *F2OrErr.get();
199
200 // Okay, now that we opened the files, scan them for the first difference.
201 const char *File1Start = F1.getBufferStart();
202 const char *File2Start = F2.getBufferStart();
203 const char *File1End = F1.getBufferEnd();
204 const char *File2End = F2.getBufferEnd();
205 const char *F1P = File1Start;
206 const char *F2P = File2Start;
207 uint64_t A_size = F1.getBufferSize();
208 uint64_t B_size = F2.getBufferSize();
209
210 // Are the buffers identical? Common case: Handle this efficiently.
211 if (A_size == B_size &&
212 std::memcmp(File1Start, File2Start, A_size) == 0)
213 return 0;
214
215 // Otherwise, we are done a tolerances are set.
216 if (AbsTol == 0 && RelTol == 0) {
217 if (Error)
218 *Error = "Files differ without tolerance allowance";
219 return 1; // Files different!
220 }
221
222 bool CompareFailed = false;
223 while (true) {
224 // Scan for the end of file or next difference.
225 while (F1P < File1End && F2P < File2End && *F1P == *F2P) {
226 ++F1P;
227 ++F2P;
228 }
229
230 if (F1P >= File1End || F2P >= File2End) break;
231
232 // Okay, we must have found a difference. Backup to the start of the
233 // current number each stream is at so that we can compare from the
234 // beginning.
235 F1P = BackupNumber(F1P, File1Start);
236 F2P = BackupNumber(F2P, File2Start);
237
238 // Now that we are at the start of the numbers, compare them, exiting if
239 // they don't match.
240 if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error)) {
241 CompareFailed = true;
242 break;
243 }
244 }
245
246 // Okay, we reached the end of file. If both files are at the end, we
247 // succeeded.
248 bool F1AtEnd = F1P >= File1End;
249 bool F2AtEnd = F2P >= File2End;
250 if (!CompareFailed && (!F1AtEnd || !F2AtEnd)) {
251 // Else, we might have run off the end due to a number: backup and retry.
252 if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
253 if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
254 F1P = BackupNumber(F1P, File1Start);
255 F2P = BackupNumber(F2P, File2Start);
256
257 // Now that we are at the start of the numbers, compare them, exiting if
258 // they don't match.
259 if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error))
260 CompareFailed = true;
261
262 // If we found the end, we succeeded.
263 if (F1P < File1End || F2P < File2End)
264 CompareFailed = true;
265 }
266
267 return CompareFailed;
268}
269
273
274 if (InputFilename != "-") {
275 if (auto EC = sys::fs::status(InputFilename, Status))
276 return createFileError(InputFilename, EC);
277 } else {
278 Status.permissions(static_cast<sys::fs::perms>(0777));
279 }
280
281 return FilePermissionsApplier(InputFilename, Status);
282}
283
285 StringRef OutputFilename, bool CopyDates,
286 std::optional<sys::fs::perms> OverwritePermissions) {
287 sys::fs::file_status Status = InputStatus;
288
289 if (OverwritePermissions)
290 Status.permissions(*OverwritePermissions);
291
292 int FD = 0;
293
294 // Writing to stdout should not be treated as an error here, just
295 // do not set access/modification times or permissions.
296 if (OutputFilename == "-")
297 return Error::success();
298
299 if (std::error_code EC = sys::fs::openFileForWrite(OutputFilename, FD,
302
303 if (CopyDates)
304 if (std::error_code EC = sys::fs::setLastAccessAndModificationTime(
305 FD, Status.getLastAccessedTime(), Status.getLastModificationTime()))
307
309 if (std::error_code EC = sys::fs::status(FD, OStat))
311 if (OStat.type() == sys::fs::file_type::regular_file) {
312#ifndef _WIN32
313 // Keep ownership if llvm-objcopy is called under root.
314 if (OutputFilename == InputFilename && OStat.getUser() == 0)
315 sys::fs::changeFileOwnership(FD, Status.getUser(), Status.getGroup());
316#endif
317
318 sys::fs::perms Perm = Status.permissions();
319 if (OutputFilename != InputFilename)
320 Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000);
321#ifdef _WIN32
322 if (std::error_code EC = sys::fs::setPermissions(OutputFilename, Perm))
323#else
324 if (std::error_code EC = sys::fs::setPermissions(FD, Perm))
325#endif
327 }
328
329 if (std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD))
331
332 return Error::success();
333}
Provides ErrorOr<T> smart pointer.
static const char * BackupNumber(const char *Pos, const char *FirstChar)
static const char * EndOfNumber(const char *Pos)
EndOfNumber - Return the first character that is not part of the specified number.
static bool isExponentChar(char C)
static bool isSignedChar(char C)
static bool isNumberChar(char C)
static bool CompareNumbers(const char *&F1P, const char *&F2P, const char *F1End, const char *F2End, double AbsTolerance, double RelTolerance, std::string *ErrorMsg)
CompareNumbers - compare two numbers, returning true if they are different.
static cl::opt< std::string > OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"))
static cl::opt< std::string > InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"))
Provides a library for accessing information about this process and other processes on the operating ...
This file defines the SmallString class.
This file contains some functions that are useful when dealing with strings.
Represents either an error or a value T.
Definition: ErrorOr.h:56
reference get()
Definition: ErrorOr.h:149
std::error_code getError() const
Definition: ErrorOr.h:152
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
FilePermssionsApplier helps to copy permissions from an input file to an output one.
Definition: FileUtilities.h:82
Error apply(StringRef OutputFilename, bool CopyDates=false, std::optional< sys::fs::perms > OverwritePermissions=std::nullopt)
Apply stored permissions to the OutputFilename.
static Expected< FilePermissionsApplier > create(StringRef InputFilename)
This interface provides simple read-only access to a block of memory, and provides simple methods for...
Definition: MemoryBuffer.h:51
size_t getBufferSize() const
Definition: MemoryBuffer.h:68
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
const char * getBufferEnd() const
Definition: MemoryBuffer.h:67
const char * getBufferStart() const
Definition: MemoryBuffer.h:66
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:661
static std::error_code SafelyCloseFileDescriptor(int FD)
Represents the result of a call to sys::fs::status().
Definition: FileSystem.h:225
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, TimePoint<> ModificationTime)
Set the file modification and access time.
std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
std::error_code changeFileOwnership(int FD, uint32_t Owner, uint32_t Group)
Change ownership of a file.
@ CD_OpenExisting
CD_OpenExisting - When opening a file:
Definition: FileSystem.h:740
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...
Definition: FileSystem.h:1062
unsigned getUmask()
Get file creation mode mask of the process.
std::error_code setPermissions(const Twine &Path, perms Permissions)
Set file permissions.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition: Error.h:1385
int DiffFilesWithTolerance(StringRef FileA, StringRef FileB, double AbsTol, double RelTol, std::string *Error=nullptr)
DiffFilesWithTolerance - Compare the two files specified, returning 0 if the files match,...