blob: 305f455937aafeed8a637437e6a2aad60bd73de2 [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
Reid Spencer551ccae2004-09-01 22:55:40 +000015#include "llvm/Support/FileUtilities.h"
16#include "llvm/Support/DataTypes.h"
Reid Spencer9d88d1a2004-12-13 17:01:53 +000017#include "llvm/System/Path.h"
Reid Spencer551ccae2004-09-01 22:55:40 +000018#include "llvm/Config/unistd.h"
19#include "llvm/Config/fcntl.h"
20#include "llvm/Config/sys/types.h"
21#include "llvm/Config/sys/stat.h"
22#include "llvm/Config/sys/mman.h"
23#include "llvm/Config/alloca.h"
Alkis Evlogimenos8868e842004-06-05 08:59:43 +000024#include <cerrno>
25#include <cstdio>
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000026#include <fstream>
27#include <iostream>
Chris Lattner2cdd21c2003-12-14 21:35:53 +000028using namespace llvm;
Brian Gaeked0fde302003-11-11 22:41:34 +000029
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000030/// DiffFiles - Compare the two files specified, returning true if they are
31/// different or if there is a file error. If you specify a string to fill in
32/// for the error option, it will set the string to an error message if an error
33/// occurs, allowing the caller to distinguish between a failed diff and a file
34/// system error.
35///
Chris Lattner2cdd21c2003-12-14 21:35:53 +000036bool llvm::DiffFiles(const std::string &FileA, const std::string &FileB,
37 std::string *Error) {
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000038 std::ifstream FileAStream(FileA.c_str());
39 if (!FileAStream) {
40 if (Error) *Error = "Couldn't open file '" + FileA + "'";
41 return true;
42 }
43
44 std::ifstream FileBStream(FileB.c_str());
45 if (!FileBStream) {
46 if (Error) *Error = "Couldn't open file '" + FileB + "'";
47 return true;
48 }
49
50 // Compare the two files...
51 int C1, C2;
52 do {
53 C1 = FileAStream.get();
54 C2 = FileBStream.get();
55 if (C1 != C2) return true;
56 } while (C1 != EOF);
57
58 return false;
59}
60
61
Chris Lattner5bfac5d2004-06-02 00:52:22 +000062/// CopyFile - Copy the specified source file to the specified destination,
63/// overwriting destination if it exists. This returns true on failure.
64///
65bool llvm::CopyFile(const std::string &Dest, const std::string &Src) {
66 FDHandle InFD(open(Src.c_str(), O_RDONLY));
67 if (InFD == -1) return true;
68
69 FileRemover FR(Dest);
70
71 FDHandle OutFD(open(Dest.c_str(), O_WRONLY|O_CREAT, 0666));
72 if (OutFD == -1) return true;
73
74 char Buffer[16*1024];
75 while (ssize_t Amt = read(InFD, Buffer, 16*1024)) {
76 if (Amt == -1) {
77 if (errno != EINTR) return true; // Error reading the file.
78 } else {
79 char *BufPtr = Buffer;
80 while (Amt) {
81 ssize_t AmtWritten = write(OutFD, BufPtr, Amt);
82 if (AmtWritten == -1) {
83 if (errno != EINTR) return true; // Error writing the file.
84 } else {
85 Amt -= AmtWritten;
86 BufPtr += AmtWritten;
87 }
88 }
89 }
90 }
91
92 FR.releaseFile(); // Success!
93 return false;
94}
95
96
Chris Lattner7a6ff2b2003-08-01 21:16:14 +000097/// MoveFileOverIfUpdated - If the file specified by New is different than Old,
98/// or if Old does not exist, move the New file over the Old file. Otherwise,
99/// remove the New file.
100///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000101void llvm::MoveFileOverIfUpdated(const std::string &New,
102 const std::string &Old) {
Chris Lattner7a6ff2b2003-08-01 21:16:14 +0000103 if (DiffFiles(New, Old)) {
104 if (std::rename(New.c_str(), Old.c_str()))
105 std::cerr << "Error renaming '" << New << "' to '" << Old << "'!\n";
106 } else {
107 std::remove(New.c_str());
108 }
109}
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000110
111/// removeFile - Delete the specified file
112///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000113void llvm::removeFile(const std::string &Filename) {
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000114 std::remove(Filename.c_str());
115}
116
117/// getUniqueFilename - Return a filename with the specified prefix. If the
118/// file does not exist yet, return it, otherwise add a suffix to make it
119/// unique.
120///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000121std::string llvm::getUniqueFilename(const std::string &FilenameBase) {
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000122 if (!std::ifstream(FilenameBase.c_str()))
123 return FilenameBase; // Couldn't open the file? Use it!
124
125 // Create a pattern for mkstemp...
126 char *FNBuffer = new char[FilenameBase.size()+8];
127 strcpy(FNBuffer, FilenameBase.c_str());
128 strcpy(FNBuffer+FilenameBase.size(), "-XXXXXX");
129
130 // Agree on a temporary file name to use....
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000131#if defined(HAVE_MKSTEMP) && !defined(_MSC_VER)
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000132 int TempFD;
133 if ((TempFD = mkstemp(FNBuffer)) == -1) {
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000134 // FIXME: this should return an emtpy string or something and allow the
135 // caller to deal with the error!
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000136 std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current "
137 << " directory!\n";
138 exit(1);
139 }
140
Misha Brukman950971d2003-09-16 15:31:46 +0000141 // We don't need to hold the temp file descriptor... we will trust that no one
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000142 // will overwrite/delete the file while we are working on it...
143 close(TempFD);
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000144#else
145 // If we don't have mkstemp, use the old and obsolete mktemp function.
146 if (mktemp(FNBuffer) == 0) {
147 // FIXME: this should return an emtpy string or something and allow the
148 // caller to deal with the error!
149 std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current "
150 << " directory!\n";
151 exit(1);
152 }
153#endif
154
Misha Brukman3d1b0c72003-08-07 21:28:50 +0000155 std::string Result(FNBuffer);
156 delete[] FNBuffer;
157 return Result;
158}
John Criswell6991a032003-09-02 20:14:57 +0000159
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000160static bool AddPermissionsBits (const std::string &Filename, int bits) {
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000161 // Get the umask value from the operating system. We want to use it
162 // when changing the file's permissions. Since calling umask() sets
163 // the umask and returns its old value, we must call it a second
164 // time to reset it to the user's preference.
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000165 int mask = umask(0777); // The arg. to umask is arbitrary.
166 umask(mask); // Restore the umask.
John Criswell6991a032003-09-02 20:14:57 +0000167
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000168 // Get the file's current mode.
169 struct stat st;
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000170 if ((stat(Filename.c_str(), &st)) == -1)
John Criswell6991a032003-09-02 20:14:57 +0000171 return false;
John Criswell6991a032003-09-02 20:14:57 +0000172
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000173 // Change the file to have whichever permissions bits from 'bits'
174 // that the umask would not disable.
175 if ((chmod(Filename.c_str(), (st.st_mode | (bits & ~mask)))) == -1)
John Criswell9adeccc2003-09-02 20:30:16 +0000176 return false;
John Criswell6991a032003-09-02 20:14:57 +0000177
178 return true;
179}
180
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000181/// MakeFileExecutable - Make the file named Filename executable by
182/// setting whichever execute permissions bits the process's current
183/// umask would allow. Filename must name an existing file or
184/// directory. Returns true on success, false on error.
John Criswell66622be2003-09-02 21:09:30 +0000185///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000186bool llvm::MakeFileExecutable(const std::string &Filename) {
187 return AddPermissionsBits(Filename, 0111);
John Criswell66622be2003-09-02 21:09:30 +0000188}
189
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000190/// MakeFileReadable - Make the file named Filename readable by
191/// setting whichever read permissions bits the process's current
192/// umask would allow. Filename must name an existing file or
193/// directory. Returns true on success, false on error.
194///
Chris Lattner2cdd21c2003-12-14 21:35:53 +0000195bool llvm::MakeFileReadable(const std::string &Filename) {
196 return AddPermissionsBits(Filename, 0444);
Brian Gaeke56be7ff2003-11-11 18:27:21 +0000197}
Chris Lattner2d6481c2003-12-29 21:35:05 +0000198
Chris Lattnereb082992004-05-28 00:23:48 +0000199/// ReadFileIntoAddressSpace - Attempt to map the specific file into the
200/// address space of the current process for reading. If this succeeds,
201/// return the address of the buffer and the length of the file mapped. On
202/// failure, return null.
203void *llvm::ReadFileIntoAddressSpace(const std::string &Filename,
204 unsigned &Length) {
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000205#if defined(HAVE_MMAP_FILE) && !defined(_MSC_VER)
Reid Spencer9d88d1a2004-12-13 17:01:53 +0000206 sys::Path File(Filename);
207 Length = (unsigned)File.getSize();
Chris Lattnereb082992004-05-28 00:23:48 +0000208 if ((int)Length == -1) return 0;
Chris Lattner81a085a2003-12-31 06:15:37 +0000209
Chris Lattnereb082992004-05-28 00:23:48 +0000210 FDHandle FD(open(Filename.c_str(), O_RDONLY));
211 if (FD == -1) return 0;
Chris Lattner81a085a2003-12-31 06:15:37 +0000212
Chris Lattnere53477e2004-05-28 00:34:42 +0000213 // If the file has a length of zero, mmap might return a null pointer. In
214 // this case, allocate a single byte of memory and return it instead.
215 if (Length == 0)
216 return malloc(1);
217
Chris Lattnereb082992004-05-28 00:23:48 +0000218 // mmap in the file all at once...
219 void *Buffer = (void*)mmap(0, Length, PROT_READ, MAP_PRIVATE, FD, 0);
220
221 if (Buffer == (void*)MAP_FAILED)
222 return 0;
Chris Lattnere53477e2004-05-28 00:34:42 +0000223
Chris Lattnereb082992004-05-28 00:23:48 +0000224 return Buffer;
225#else
226 // FIXME: implement with read/write
Reid Spencerd2324382004-09-20 04:13:43 +0000227#error Unimplemented ReadFileIntoAddressSpace - need to use read/write.
Chris Lattnereb082992004-05-28 00:23:48 +0000228 return 0;
229#endif
230}
231
232/// UnmapFileFromAddressSpace - Remove the specified file from the current
233/// address space.
234void llvm::UnmapFileFromAddressSpace(void *Buffer, unsigned Length) {
Chris Lattnerfa5fe7c2004-06-07 19:37:24 +0000235#if defined(HAVE_MMAP_FILE) && !defined(_MSC_VER)
Chris Lattnere53477e2004-05-28 00:34:42 +0000236 if (Length)
237 munmap((char*)Buffer, Length);
238 else
239 free(Buffer); // Zero byte files are malloc(1)'s.
Chris Lattnereb082992004-05-28 00:23:48 +0000240#else
241 free(Buffer);
242#endif
243}
Chris Lattner316cb082003-12-30 07:36:14 +0000244
Chris Lattner2d6481c2003-12-29 21:35:05 +0000245//===----------------------------------------------------------------------===//
246// FDHandle class implementation
247//
248
Chris Lattner9b448b72003-12-29 21:43:58 +0000249FDHandle::~FDHandle() throw() {
Chris Lattner2d6481c2003-12-29 21:35:05 +0000250 if (FD != -1) close(FD);
251}
252
Chris Lattner9b448b72003-12-29 21:43:58 +0000253FDHandle &FDHandle::operator=(int fd) throw() {
Chris Lattner2d6481c2003-12-29 21:35:05 +0000254 if (FD != -1) close(FD);
255 FD = fd;
256 return *this;
257}
258