1009b1c42SEd Schouten //===- Support/FileUtilities.cpp - File System Utilities ------------------===//
2009b1c42SEd Schouten //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6009b1c42SEd Schouten //
7009b1c42SEd Schouten //===----------------------------------------------------------------------===//
8009b1c42SEd Schouten //
9009b1c42SEd Schouten // This file implements a family of utility functions which are useful for doing
10009b1c42SEd Schouten // various things with files.
11009b1c42SEd Schouten //
12009b1c42SEd Schouten //===----------------------------------------------------------------------===//
13009b1c42SEd Schouten
14009b1c42SEd Schouten #include "llvm/Support/FileUtilities.h"
15009b1c42SEd Schouten #include "llvm/ADT/SmallString.h"
16cfca06d7SDimitry Andric #include "llvm/ADT/StringExtras.h"
171d5ae102SDimitry Andric #include "llvm/Support/Error.h"
18b915e9e0SDimitry Andric #include "llvm/Support/ErrorOr.h"
194a16efa3SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
20145449b1SDimitry Andric #include "llvm/Support/Process.h"
214a16efa3SDimitry Andric #include "llvm/Support/raw_ostream.h"
22e3b55780SDimitry Andric #include <cmath>
23b915e9e0SDimitry Andric #include <cstdint>
24009b1c42SEd Schouten #include <cstdlib>
25009b1c42SEd Schouten #include <cstring>
26b915e9e0SDimitry Andric #include <memory>
275ca98fd9SDimitry Andric #include <system_error>
28b915e9e0SDimitry Andric
29009b1c42SEd Schouten using namespace llvm;
30009b1c42SEd Schouten
isSignedChar(char C)31009b1c42SEd Schouten static bool isSignedChar(char C) {
32009b1c42SEd Schouten return (C == '+' || C == '-');
33009b1c42SEd Schouten }
34009b1c42SEd Schouten
isExponentChar(char C)35009b1c42SEd Schouten static bool isExponentChar(char C) {
36009b1c42SEd Schouten switch (C) {
37009b1c42SEd Schouten case 'D': // Strange exponential notation.
38009b1c42SEd Schouten case 'd': // Strange exponential notation.
39009b1c42SEd Schouten case 'e':
40009b1c42SEd Schouten case 'E': return true;
41009b1c42SEd Schouten default: return false;
42009b1c42SEd Schouten }
43009b1c42SEd Schouten }
44009b1c42SEd Schouten
isNumberChar(char C)45009b1c42SEd Schouten static bool isNumberChar(char C) {
46009b1c42SEd Schouten switch (C) {
47009b1c42SEd Schouten case '0': case '1': case '2': case '3': case '4':
48009b1c42SEd Schouten case '5': case '6': case '7': case '8': case '9':
49009b1c42SEd Schouten case '.': return true;
50009b1c42SEd Schouten default: return isSignedChar(C) || isExponentChar(C);
51009b1c42SEd Schouten }
52009b1c42SEd Schouten }
53009b1c42SEd Schouten
BackupNumber(const char * Pos,const char * FirstChar)54009b1c42SEd Schouten static const char *BackupNumber(const char *Pos, const char *FirstChar) {
55009b1c42SEd Schouten // If we didn't stop in the middle of a number, don't backup.
56009b1c42SEd Schouten if (!isNumberChar(*Pos)) return Pos;
57009b1c42SEd Schouten
58009b1c42SEd Schouten // Otherwise, return to the start of the number.
5966e41e3cSRoman Divacky bool HasPeriod = false;
60009b1c42SEd Schouten while (Pos > FirstChar && isNumberChar(Pos[-1])) {
6166e41e3cSRoman Divacky // Backup over at most one period.
6266e41e3cSRoman Divacky if (Pos[-1] == '.') {
6366e41e3cSRoman Divacky if (HasPeriod)
6466e41e3cSRoman Divacky break;
6566e41e3cSRoman Divacky HasPeriod = true;
6666e41e3cSRoman Divacky }
6766e41e3cSRoman Divacky
68009b1c42SEd Schouten --Pos;
69009b1c42SEd Schouten if (Pos > FirstChar && isSignedChar(Pos[0]) && !isExponentChar(Pos[-1]))
70009b1c42SEd Schouten break;
71009b1c42SEd Schouten }
72009b1c42SEd Schouten return Pos;
73009b1c42SEd Schouten }
74009b1c42SEd Schouten
75009b1c42SEd Schouten /// EndOfNumber - Return the first character that is not part of the specified
76009b1c42SEd Schouten /// number. This assumes that the buffer is null terminated, so it won't fall
77009b1c42SEd Schouten /// off the end.
EndOfNumber(const char * Pos)78009b1c42SEd Schouten static const char *EndOfNumber(const char *Pos) {
79009b1c42SEd Schouten while (isNumberChar(*Pos))
80009b1c42SEd Schouten ++Pos;
81009b1c42SEd Schouten return Pos;
82009b1c42SEd Schouten }
83009b1c42SEd Schouten
84009b1c42SEd Schouten /// CompareNumbers - compare two numbers, returning true if they are different.
CompareNumbers(const char * & F1P,const char * & F2P,const char * F1End,const char * F2End,double AbsTolerance,double RelTolerance,std::string * ErrorMsg)85009b1c42SEd Schouten static bool CompareNumbers(const char *&F1P, const char *&F2P,
86009b1c42SEd Schouten const char *F1End, const char *F2End,
87009b1c42SEd Schouten double AbsTolerance, double RelTolerance,
88009b1c42SEd Schouten std::string *ErrorMsg) {
89009b1c42SEd Schouten const char *F1NumEnd, *F2NumEnd;
90009b1c42SEd Schouten double V1 = 0.0, V2 = 0.0;
91009b1c42SEd Schouten
92009b1c42SEd Schouten // If one of the positions is at a space and the other isn't, chomp up 'til
93009b1c42SEd Schouten // the end of the space.
94cfca06d7SDimitry Andric while (isSpace(static_cast<unsigned char>(*F1P)) && F1P != F1End)
95009b1c42SEd Schouten ++F1P;
96cfca06d7SDimitry Andric while (isSpace(static_cast<unsigned char>(*F2P)) && F2P != F2End)
97009b1c42SEd Schouten ++F2P;
98009b1c42SEd Schouten
99009b1c42SEd Schouten // If we stop on numbers, compare their difference.
100009b1c42SEd Schouten if (!isNumberChar(*F1P) || !isNumberChar(*F2P)) {
101009b1c42SEd Schouten // The diff failed.
102009b1c42SEd Schouten F1NumEnd = F1P;
103009b1c42SEd Schouten F2NumEnd = F2P;
104009b1c42SEd Schouten } else {
105009b1c42SEd Schouten // Note that some ugliness is built into this to permit support for numbers
106009b1c42SEd Schouten // that use "D" or "d" as their exponential marker, e.g. "1.234D45". This
107009b1c42SEd Schouten // occurs in 200.sixtrack in spec2k.
108009b1c42SEd Schouten V1 = strtod(F1P, const_cast<char**>(&F1NumEnd));
109009b1c42SEd Schouten V2 = strtod(F2P, const_cast<char**>(&F2NumEnd));
110009b1c42SEd Schouten
111009b1c42SEd Schouten if (*F1NumEnd == 'D' || *F1NumEnd == 'd') {
112009b1c42SEd Schouten // Copy string into tmp buffer to replace the 'D' with an 'e'.
113009b1c42SEd Schouten SmallString<200> StrTmp(F1P, EndOfNumber(F1NumEnd)+1);
114009b1c42SEd Schouten // Strange exponential notation!
115009b1c42SEd Schouten StrTmp[static_cast<unsigned>(F1NumEnd-F1P)] = 'e';
116009b1c42SEd Schouten
117009b1c42SEd Schouten V1 = strtod(&StrTmp[0], const_cast<char**>(&F1NumEnd));
118009b1c42SEd Schouten F1NumEnd = F1P + (F1NumEnd-&StrTmp[0]);
119009b1c42SEd Schouten }
120009b1c42SEd Schouten
121009b1c42SEd Schouten if (*F2NumEnd == 'D' || *F2NumEnd == 'd') {
122009b1c42SEd Schouten // Copy string into tmp buffer to replace the 'D' with an 'e'.
123009b1c42SEd Schouten SmallString<200> StrTmp(F2P, EndOfNumber(F2NumEnd)+1);
124009b1c42SEd Schouten // Strange exponential notation!
125009b1c42SEd Schouten StrTmp[static_cast<unsigned>(F2NumEnd-F2P)] = 'e';
126009b1c42SEd Schouten
127009b1c42SEd Schouten V2 = strtod(&StrTmp[0], const_cast<char**>(&F2NumEnd));
128009b1c42SEd Schouten F2NumEnd = F2P + (F2NumEnd-&StrTmp[0]);
129009b1c42SEd Schouten }
130009b1c42SEd Schouten }
131009b1c42SEd Schouten
132009b1c42SEd Schouten if (F1NumEnd == F1P || F2NumEnd == F2P) {
133009b1c42SEd Schouten if (ErrorMsg) {
134009b1c42SEd Schouten *ErrorMsg = "FP Comparison failed, not a numeric difference between '";
135009b1c42SEd Schouten *ErrorMsg += F1P[0];
136009b1c42SEd Schouten *ErrorMsg += "' and '";
137009b1c42SEd Schouten *ErrorMsg += F2P[0];
138009b1c42SEd Schouten *ErrorMsg += "'";
139009b1c42SEd Schouten }
140009b1c42SEd Schouten return true;
141009b1c42SEd Schouten }
142009b1c42SEd Schouten
143009b1c42SEd Schouten // Check to see if these are inside the absolute tolerance
144009b1c42SEd Schouten if (AbsTolerance < std::abs(V1-V2)) {
145009b1c42SEd Schouten // Nope, check the relative tolerance...
146009b1c42SEd Schouten double Diff;
147009b1c42SEd Schouten if (V2)
148009b1c42SEd Schouten Diff = std::abs(V1/V2 - 1.0);
149009b1c42SEd Schouten else if (V1)
150009b1c42SEd Schouten Diff = std::abs(V2/V1 - 1.0);
151009b1c42SEd Schouten else
152009b1c42SEd Schouten Diff = 0; // Both zero.
153009b1c42SEd Schouten if (Diff > RelTolerance) {
154009b1c42SEd Schouten if (ErrorMsg) {
1556fe5c7aaSRoman Divacky raw_string_ostream(*ErrorMsg)
1566fe5c7aaSRoman Divacky << "Compared: " << V1 << " and " << V2 << '\n'
1576fe5c7aaSRoman Divacky << "abs. diff = " << std::abs(V1-V2) << " rel.diff = " << Diff << '\n'
1586fe5c7aaSRoman Divacky << "Out of tolerance: rel/abs: " << RelTolerance << '/'
1596fe5c7aaSRoman Divacky << AbsTolerance;
160009b1c42SEd Schouten }
161009b1c42SEd Schouten return true;
162009b1c42SEd Schouten }
163009b1c42SEd Schouten }
164009b1c42SEd Schouten
165009b1c42SEd Schouten // Otherwise, advance our read pointers to the end of the numbers.
166009b1c42SEd Schouten F1P = F1NumEnd; F2P = F2NumEnd;
167009b1c42SEd Schouten return false;
168009b1c42SEd Schouten }
169009b1c42SEd Schouten
170009b1c42SEd Schouten /// DiffFilesWithTolerance - Compare the two files specified, returning 0 if the
171009b1c42SEd Schouten /// files match, 1 if they are different, and 2 if there is a file error. This
1727fa27ce4SDimitry Andric /// function differs from DiffFiles in that you can specify an absolute and
173009b1c42SEd Schouten /// relative FP error that is allowed to exist. If you specify a string to fill
174009b1c42SEd Schouten /// in for the error option, it will set the string to an error message if an
175009b1c42SEd Schouten /// error occurs, allowing the caller to distinguish between a failed diff and a
176009b1c42SEd Schouten /// file system error.
177009b1c42SEd Schouten ///
DiffFilesWithTolerance(StringRef NameA,StringRef NameB,double AbsTol,double RelTol,std::string * Error)178f8af5cf6SDimitry Andric int llvm::DiffFilesWithTolerance(StringRef NameA,
179f8af5cf6SDimitry Andric StringRef NameB,
180009b1c42SEd Schouten double AbsTol, double RelTol,
181009b1c42SEd Schouten std::string *Error) {
1826b943ff3SDimitry Andric // Now its safe to mmap the files into memory because both files
183009b1c42SEd Schouten // have a non-zero size.
1845ca98fd9SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> F1OrErr = MemoryBuffer::getFile(NameA);
1855ca98fd9SDimitry Andric if (std::error_code EC = F1OrErr.getError()) {
186cf099d11SDimitry Andric if (Error)
1875ca98fd9SDimitry Andric *Error = EC.message();
188009b1c42SEd Schouten return 2;
189cf099d11SDimitry Andric }
19067c32a98SDimitry Andric MemoryBuffer &F1 = *F1OrErr.get();
1915ca98fd9SDimitry Andric
1925ca98fd9SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> F2OrErr = MemoryBuffer::getFile(NameB);
1935ca98fd9SDimitry Andric if (std::error_code EC = F2OrErr.getError()) {
194cf099d11SDimitry Andric if (Error)
1955ca98fd9SDimitry Andric *Error = EC.message();
196cf099d11SDimitry Andric return 2;
197cf099d11SDimitry Andric }
19867c32a98SDimitry Andric MemoryBuffer &F2 = *F2OrErr.get();
199009b1c42SEd Schouten
200009b1c42SEd Schouten // Okay, now that we opened the files, scan them for the first difference.
20167c32a98SDimitry Andric const char *File1Start = F1.getBufferStart();
20267c32a98SDimitry Andric const char *File2Start = F2.getBufferStart();
20367c32a98SDimitry Andric const char *File1End = F1.getBufferEnd();
20467c32a98SDimitry Andric const char *File2End = F2.getBufferEnd();
205009b1c42SEd Schouten const char *F1P = File1Start;
206009b1c42SEd Schouten const char *F2P = File2Start;
20767c32a98SDimitry Andric uint64_t A_size = F1.getBufferSize();
20867c32a98SDimitry Andric uint64_t B_size = F2.getBufferSize();
209009b1c42SEd Schouten
210009b1c42SEd Schouten // Are the buffers identical? Common case: Handle this efficiently.
21166e41e3cSRoman Divacky if (A_size == B_size &&
21266e41e3cSRoman Divacky std::memcmp(File1Start, File2Start, A_size) == 0)
213009b1c42SEd Schouten return 0;
214009b1c42SEd Schouten
21566e41e3cSRoman Divacky // Otherwise, we are done a tolerances are set.
216009b1c42SEd Schouten if (AbsTol == 0 && RelTol == 0) {
217009b1c42SEd Schouten if (Error)
218009b1c42SEd Schouten *Error = "Files differ without tolerance allowance";
219009b1c42SEd Schouten return 1; // Files different!
220009b1c42SEd Schouten }
221009b1c42SEd Schouten
222009b1c42SEd Schouten bool CompareFailed = false;
223b915e9e0SDimitry Andric while (true) {
224009b1c42SEd Schouten // Scan for the end of file or next difference.
22501095a5dSDimitry Andric while (F1P < File1End && F2P < File2End && *F1P == *F2P) {
22601095a5dSDimitry Andric ++F1P;
22701095a5dSDimitry Andric ++F2P;
22801095a5dSDimitry Andric }
229009b1c42SEd Schouten
230009b1c42SEd Schouten if (F1P >= File1End || F2P >= File2End) break;
231009b1c42SEd Schouten
232009b1c42SEd Schouten // Okay, we must have found a difference. Backup to the start of the
233009b1c42SEd Schouten // current number each stream is at so that we can compare from the
234009b1c42SEd Schouten // beginning.
235009b1c42SEd Schouten F1P = BackupNumber(F1P, File1Start);
236009b1c42SEd Schouten F2P = BackupNumber(F2P, File2Start);
237009b1c42SEd Schouten
238009b1c42SEd Schouten // Now that we are at the start of the numbers, compare them, exiting if
239009b1c42SEd Schouten // they don't match.
240009b1c42SEd Schouten if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error)) {
241009b1c42SEd Schouten CompareFailed = true;
242009b1c42SEd Schouten break;
243009b1c42SEd Schouten }
244009b1c42SEd Schouten }
245009b1c42SEd Schouten
246009b1c42SEd Schouten // Okay, we reached the end of file. If both files are at the end, we
247009b1c42SEd Schouten // succeeded.
248009b1c42SEd Schouten bool F1AtEnd = F1P >= File1End;
249009b1c42SEd Schouten bool F2AtEnd = F2P >= File2End;
250009b1c42SEd Schouten if (!CompareFailed && (!F1AtEnd || !F2AtEnd)) {
251009b1c42SEd Schouten // Else, we might have run off the end due to a number: backup and retry.
252009b1c42SEd Schouten if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
253009b1c42SEd Schouten if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
254009b1c42SEd Schouten F1P = BackupNumber(F1P, File1Start);
255009b1c42SEd Schouten F2P = BackupNumber(F2P, File2Start);
256009b1c42SEd Schouten
257009b1c42SEd Schouten // Now that we are at the start of the numbers, compare them, exiting if
258009b1c42SEd Schouten // they don't match.
259009b1c42SEd Schouten if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error))
260009b1c42SEd Schouten CompareFailed = true;
261009b1c42SEd Schouten
262009b1c42SEd Schouten // If we found the end, we succeeded.
263009b1c42SEd Schouten if (F1P < File1End || F2P < File2End)
264009b1c42SEd Schouten CompareFailed = true;
265009b1c42SEd Schouten }
266009b1c42SEd Schouten
267009b1c42SEd Schouten return CompareFailed;
268009b1c42SEd Schouten }
2691d5ae102SDimitry Andric
270145449b1SDimitry Andric Expected<FilePermissionsApplier>
create(StringRef InputFilename)271145449b1SDimitry Andric FilePermissionsApplier::create(StringRef InputFilename) {
272145449b1SDimitry Andric sys::fs::file_status Status;
273145449b1SDimitry Andric
274145449b1SDimitry Andric if (InputFilename != "-") {
275145449b1SDimitry Andric if (auto EC = sys::fs::status(InputFilename, Status))
276145449b1SDimitry Andric return createFileError(InputFilename, EC);
277145449b1SDimitry Andric } else {
278145449b1SDimitry Andric Status.permissions(static_cast<sys::fs::perms>(0777));
279145449b1SDimitry Andric }
280145449b1SDimitry Andric
281145449b1SDimitry Andric return FilePermissionsApplier(InputFilename, Status);
282145449b1SDimitry Andric }
283145449b1SDimitry Andric
apply(StringRef OutputFilename,bool CopyDates,std::optional<sys::fs::perms> OverwritePermissions)284145449b1SDimitry Andric Error FilePermissionsApplier::apply(
285145449b1SDimitry Andric StringRef OutputFilename, bool CopyDates,
286e3b55780SDimitry Andric std::optional<sys::fs::perms> OverwritePermissions) {
287145449b1SDimitry Andric sys::fs::file_status Status = InputStatus;
288145449b1SDimitry Andric
289145449b1SDimitry Andric if (OverwritePermissions)
290145449b1SDimitry Andric Status.permissions(*OverwritePermissions);
291145449b1SDimitry Andric
292145449b1SDimitry Andric int FD = 0;
293145449b1SDimitry Andric
294145449b1SDimitry Andric // Writing to stdout should not be treated as an error here, just
295145449b1SDimitry Andric // do not set access/modification times or permissions.
296145449b1SDimitry Andric if (OutputFilename == "-")
297145449b1SDimitry Andric return Error::success();
298145449b1SDimitry Andric
299145449b1SDimitry Andric if (std::error_code EC = sys::fs::openFileForWrite(OutputFilename, FD,
300145449b1SDimitry Andric sys::fs::CD_OpenExisting))
301145449b1SDimitry Andric return createFileError(OutputFilename, EC);
302145449b1SDimitry Andric
303145449b1SDimitry Andric if (CopyDates)
304145449b1SDimitry Andric if (std::error_code EC = sys::fs::setLastAccessAndModificationTime(
305145449b1SDimitry Andric FD, Status.getLastAccessedTime(), Status.getLastModificationTime()))
306145449b1SDimitry Andric return createFileError(OutputFilename, EC);
307145449b1SDimitry Andric
308145449b1SDimitry Andric sys::fs::file_status OStat;
309145449b1SDimitry Andric if (std::error_code EC = sys::fs::status(FD, OStat))
310145449b1SDimitry Andric return createFileError(OutputFilename, EC);
311145449b1SDimitry Andric if (OStat.type() == sys::fs::file_type::regular_file) {
312145449b1SDimitry Andric #ifndef _WIN32
313145449b1SDimitry Andric // Keep ownership if llvm-objcopy is called under root.
314145449b1SDimitry Andric if (OutputFilename == InputFilename && OStat.getUser() == 0)
315145449b1SDimitry Andric sys::fs::changeFileOwnership(FD, Status.getUser(), Status.getGroup());
316145449b1SDimitry Andric #endif
317145449b1SDimitry Andric
318145449b1SDimitry Andric sys::fs::perms Perm = Status.permissions();
319145449b1SDimitry Andric if (OutputFilename != InputFilename)
320145449b1SDimitry Andric Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000);
321145449b1SDimitry Andric #ifdef _WIN32
322145449b1SDimitry Andric if (std::error_code EC = sys::fs::setPermissions(OutputFilename, Perm))
323145449b1SDimitry Andric #else
324145449b1SDimitry Andric if (std::error_code EC = sys::fs::setPermissions(FD, Perm))
325145449b1SDimitry Andric #endif
326145449b1SDimitry Andric return createFileError(OutputFilename, EC);
327145449b1SDimitry Andric }
328145449b1SDimitry Andric
329145449b1SDimitry Andric if (std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD))
330145449b1SDimitry Andric return createFileError(OutputFilename, EC);
331145449b1SDimitry Andric
332145449b1SDimitry Andric return Error::success();
333145449b1SDimitry Andric }
334