blob: 9844835daa20332bd8f1ed0a9f4d23cc6b3c2ed8 [file] [log] [blame]
Chris Lattner7a6ff2b2003-08-01 21:16:14 +00001//===- Support/FileUtilities.cpp - File System Utilities ------------------===//
John Criswellb576c942003-10-20 19:43:21 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by the LLVM research group and is distributed under
6// the University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
Chris Lattner7a6ff2b2003-08-01 21:16:14 +00009//
10// This file implements a family of utility functions which are useful for doing
11// various things with files.
12//
13//===----------------------------------------------------------------------===//
14
15#include "Support/FileUtilities.h"
Misha Brukmanda81eca2003-08-07 21:35:41 +000016#include "Config/unistd.h"
Chris Lattnereb082992004-05-28 00:23:48 +000017#include "Config/fcntl.h"
John Criswell6991a032003-09-02 20:14:57 +000018#include "Config/sys/stat.h"
19#include "Config/sys/types.h"
Chris Lattnereb082992004-05-28 00:23:48 +000020#include "Config/sys/mman.h"
Alkis Evlogimenos8868e842004-06-05 08:59:43 +000021#include <cerrno>
22#include <cstdio>
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000023#include <fstream>
24#include <iostream>
Chris Lattner2cdd21c2003-12-14 21:35:53 +000025using namespace llvm;
Brian Gaeked0fde302003-11-11 22:41:34 +000026
Brian Gaekea2302ff2003-11-11 21:53:50 +000027/// CheckMagic - Returns true IFF the file named FN begins with Magic. FN must
28/// name a readable file.
29///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000030bool llvm::CheckMagic(const std::string &FN, const std::string &Magic) {
Brian Gaekea2302ff2003-11-11 21:53:50 +000031 char buf[1 + Magic.size ()];
32 std::ifstream f (FN.c_str ());
33 f.read (buf, Magic.size ());
34 buf[Magic.size ()] = '\0';
35 return Magic == buf;
36}
37
38/// IsArchive - Returns true IFF the file named FN appears to be a "ar" library
39/// archive. The file named FN must exist.
40///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000041bool llvm::IsArchive(const std::string &FN) {
Brian Gaekea2302ff2003-11-11 21:53:50 +000042 // Inspect the beginning of the file to see if it contains the "ar"
43 // library archive format magic string.
44 return CheckMagic (FN, "!<arch>\012");
45}
46
47/// IsBytecode - Returns true IFF the file named FN appears to be an LLVM
48/// bytecode file. The file named FN must exist.
49///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000050bool llvm::IsBytecode(const std::string &FN) {
Brian Gaekea2302ff2003-11-11 21:53:50 +000051 // Inspect the beginning of the file to see if it contains the LLVM
52 // bytecode format magic string.
53 return CheckMagic (FN, "llvm");
54}
55
Misha Brukman17cca962003-11-24 05:28:12 +000056/// IsSharedObject - Returns trus IFF the file named FN appears to be a shared
57/// object with an ELF header. The file named FN must exist.
58///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000059bool llvm::IsSharedObject(const std::string &FN) {
Misha Brukman971a7b82003-11-24 05:36:38 +000060 // Inspect the beginning of the file to see if it contains the ELF shared
61 // object magic string.
Misha Brukman17cca962003-11-24 05:28:12 +000062 static const char elfMagic[] = { 0x7f, 'E', 'L', 'F', '\0' };
63 return CheckMagic(FN, elfMagic);
64}
65
Brian Gaeke56be7ff2003-11-11 18:27:21 +000066/// FileOpenable - Returns true IFF Filename names an existing regular
67/// file which we can successfully open.
68///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000069bool llvm::FileOpenable(const std::string &Filename) {
Brian Gaeke56be7ff2003-11-11 18:27:21 +000070 struct stat s;
71 if (stat (Filename.c_str (), &s) == -1)
72 return false; // Cannot stat file
73 if (!S_ISREG (s.st_mode))
74 return false; // File is not a regular file
75 std::ifstream FileStream (Filename.c_str ());
76 if (!FileStream)
77 return false; // File is not openable
78 return true;
79}
80
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000081/// DiffFiles - Compare the two files specified, returning true if they are
82/// different or if there is a file error. If you specify a string to fill in
83/// for the error option, it will set the string to an error message if an error
84/// occurs, allowing the caller to distinguish between a failed diff and a file
85/// system error.
86///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000087bool llvm::DiffFiles(const std::string &FileA, const std::string &FileB,
88 std::string *Error) {
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000089 std::ifstream FileAStream(FileA.c_str());
90 if (!FileAStream) {
91 if (Error) *Error = "Couldn't open file '" + FileA + "'";
92 return true;
93 }
94
95 std::ifstream FileBStream(FileB.c_str());
96 if (!FileBStream) {
97 if (Error) *Error = "Couldn't open file '" + FileB + "'";
98 return true;
99 }
100
101 // Compare the two files...
102 int C1, C2;
103 do {
104 C1 = FileAStream.get();
105 C2 = FileBStream.get();
106 if (C1 != C2) return true;
107 } while (C1 != EOF);
108
109 return false;
110}
111
112
Chris Lattner5bfac5d2004-06-02 00:52:22 +0000113/// CopyFile - Copy the specified source file to the specified destination,
114/// overwriting destination if it exists. This returns true on failure.
115///
116bool llvm::CopyFile(const std::string &Dest, const std::string &Src) {
117 FDHandle InFD(open(Src.c_str(), O_RDONLY));
118 if (InFD == -1) return true;
119
120 FileRemover FR(Dest);
121
122 FDHandle OutFD(open(Dest.c_str(), O_WRONLY|O_CREAT, 0666));
123 if (OutFD == -1) return true;
124
125 char Buffer[16*1024];
126 while (ssize_t Amt = read(InFD, Buffer, 16*1024)) {
127 if (Amt == -1) {
128 if (errno != EINTR) return true; // Error reading the file.
129 } else {
130 char *BufPtr = Buffer;
131 while (Amt) {
132 ssize_t AmtWritten = write(OutFD, BufPtr, Amt);
133 if (AmtWritten == -1) {
134 if (errno != EINTR) return true; // Error writing the file.
135 } else {
136 Amt -= AmtWritten;
137 BufPtr += AmtWritten;
138 }
139 }
140 }
141 }
142
143 FR.releaseFile(); // Success!
144 return false;
145}
146
147
Chris Lattner7a6ff2b2003-08-01 21:16:14 +0000148/// MoveFileOverIfUpdated - If the file specified by New is different than Old,
149/// or if Old does not exist, move the New file over the Old file. Otherwise,
150/// remove the New file.
151///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000152void llvm::MoveFileOverIfUpdated(const std::string &New,
153 const std::string &Old) {
Chris Lattner7a6ff2b2003-08-01 21:16:14 +0000154 if (DiffFiles(New, Old)) {
155 if (std::rename(New.c_str(), Old.c_str()))
156 std::cerr << "Error renaming '" << New << "' to '" << Old << "'!\n";
157 } else {
158 std::remove(New.c_str());
159 }
160}
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000161
162/// removeFile - Delete the specified file
163///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000164void llvm::removeFile(const std::string &Filename) {
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000165 std::remove(Filename.c_str());
166}
167
168/// getUniqueFilename - Return a filename with the specified prefix. If the
169/// file does not exist yet, return it, otherwise add a suffix to make it
170/// unique.
171///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000172std::string llvm::getUniqueFilename(const std::string &FilenameBase) {
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000173 if (!std::ifstream(FilenameBase.c_str()))
174 return FilenameBase; // Couldn't open the file? Use it!
175
176 // Create a pattern for mkstemp...
177 char *FNBuffer = new char[FilenameBase.size()+8];
178 strcpy(FNBuffer, FilenameBase.c_str());
179 strcpy(FNBuffer+FilenameBase.size(), "-XXXXXX");
180
181 // Agree on a temporary file name to use....
182 int TempFD;
183 if ((TempFD = mkstemp(FNBuffer)) == -1) {
184 std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current "
185 << " directory!\n";
186 exit(1);
187 }
188
Misha Brukman950971d2003-09-16 15:31:46 +0000189 // We don't need to hold the temp file descriptor... we will trust that no one
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000190 // will overwrite/delete the file while we are working on it...
191 close(TempFD);
192 std::string Result(FNBuffer);
193 delete[] FNBuffer;
194 return Result;
195}
John Criswell6991a032003-09-02 20:14:57 +0000196
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000197static bool AddPermissionsBits (const std::string &Filename, mode_t bits) {
198 // Get the umask value from the operating system. We want to use it
199 // when changing the file's permissions. Since calling umask() sets
200 // the umask and returns its old value, we must call it a second
201 // time to reset it to the user's preference.
202 mode_t mask = umask (0777); // The arg. to umask is arbitrary...
John Criswell6991a032003-09-02 20:14:57 +0000203 umask (mask);
204
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000205 // Get the file's current mode.
206 struct stat st;
207 if ((stat (Filename.c_str(), &st)) == -1)
John Criswell6991a032003-09-02 20:14:57 +0000208 return false;
John Criswell6991a032003-09-02 20:14:57 +0000209
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000210 // Change the file to have whichever permissions bits from 'bits'
211 // that the umask would not disable.
212 if ((chmod(Filename.c_str(), (st.st_mode | (bits & ~mask)))) == -1)
John Criswell9adeccc2003-09-02 20:30:16 +0000213 return false;
John Criswell6991a032003-09-02 20:14:57 +0000214
215 return true;
216}
217
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000218/// MakeFileExecutable - Make the file named Filename executable by
219/// setting whichever execute permissions bits the process's current
220/// umask would allow. Filename must name an existing file or
221/// directory. Returns true on success, false on error.
John Criswell66622be2003-09-02 21:09:30 +0000222///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000223bool llvm::MakeFileExecutable(const std::string &Filename) {
224 return AddPermissionsBits(Filename, 0111);
John Criswell66622be2003-09-02 21:09:30 +0000225}
226
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000227/// MakeFileReadable - Make the file named Filename readable by
228/// setting whichever read permissions bits the process's current
229/// umask would allow. Filename must name an existing file or
230/// directory. Returns true on success, false on error.
231///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000232bool llvm::MakeFileReadable(const std::string &Filename) {
233 return AddPermissionsBits(Filename, 0444);
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000234}
Chris Lattner2d6481c2003-12-29 21:35:05 +0000235
Chris Lattner316cb082003-12-30 07:36:14 +0000236/// getFileSize - Return the size of the specified file in bytes, or -1 if the
237/// file cannot be read or does not exist.
238long long llvm::getFileSize(const std::string &Filename) {
239 struct stat StatBuf;
240 if (stat(Filename.c_str(), &StatBuf) == -1)
241 return -1;
242 return StatBuf.st_size;
243}
244
Chris Lattner81a085a2003-12-31 06:15:37 +0000245/// getFileTimestamp - Get the last modified time for the specified file in an
246/// unspecified format. This is useful to allow checking to see if a file was
247/// updated since that last time the timestampt was aquired. If the file does
248/// not exist or there is an error getting the time-stamp, zero is returned.
249unsigned long long llvm::getFileTimestamp(const std::string &Filename) {
250 struct stat StatBuf;
251 if (stat(Filename.c_str(), &StatBuf) == -1)
252 return 0;
253 return StatBuf.st_mtime;
254}
255
Chris Lattnereb082992004-05-28 00:23:48 +0000256/// ReadFileIntoAddressSpace - Attempt to map the specific file into the
257/// address space of the current process for reading. If this succeeds,
258/// return the address of the buffer and the length of the file mapped. On
259/// failure, return null.
260void *llvm::ReadFileIntoAddressSpace(const std::string &Filename,
261 unsigned &Length) {
262#ifdef HAVE_MMAP_FILE
263 Length = getFileSize(Filename);
264 if ((int)Length == -1) return 0;
Chris Lattner81a085a2003-12-31 06:15:37 +0000265
Chris Lattnereb082992004-05-28 00:23:48 +0000266 FDHandle FD(open(Filename.c_str(), O_RDONLY));
267 if (FD == -1) return 0;
Chris Lattner81a085a2003-12-31 06:15:37 +0000268
Chris Lattnere53477e2004-05-28 00:34:42 +0000269 // If the file has a length of zero, mmap might return a null pointer. In
270 // this case, allocate a single byte of memory and return it instead.
271 if (Length == 0)
272 return malloc(1);
273
Chris Lattnereb082992004-05-28 00:23:48 +0000274 // mmap in the file all at once...
275 void *Buffer = (void*)mmap(0, Length, PROT_READ, MAP_PRIVATE, FD, 0);
276
277 if (Buffer == (void*)MAP_FAILED)
278 return 0;
Chris Lattnere53477e2004-05-28 00:34:42 +0000279
Chris Lattnereb082992004-05-28 00:23:48 +0000280 return Buffer;
281#else
282 // FIXME: implement with read/write
283 return 0;
284#endif
285}
286
287/// UnmapFileFromAddressSpace - Remove the specified file from the current
288/// address space.
289void llvm::UnmapFileFromAddressSpace(void *Buffer, unsigned Length) {
290#ifdef HAVE_MMAP_FILE
Chris Lattnere53477e2004-05-28 00:34:42 +0000291 if (Length)
292 munmap((char*)Buffer, Length);
293 else
294 free(Buffer); // Zero byte files are malloc(1)'s.
Chris Lattnereb082992004-05-28 00:23:48 +0000295#else
296 free(Buffer);
297#endif
298}
Chris Lattner316cb082003-12-30 07:36:14 +0000299
Chris Lattner2d6481c2003-12-29 21:35:05 +0000300//===----------------------------------------------------------------------===//
301// FDHandle class implementation
302//
303
Chris Lattner9b448b72003-12-29 21:43:58 +0000304FDHandle::~FDHandle() throw() {
Chris Lattner2d6481c2003-12-29 21:35:05 +0000305 if (FD != -1) close(FD);
306}
307
Chris Lattner9b448b72003-12-29 21:43:58 +0000308FDHandle &FDHandle::operator=(int fd) throw() {
Chris Lattner2d6481c2003-12-29 21:35:05 +0000309 if (FD != -1) close(FD);
310 FD = fd;
311 return *this;
312}
313