LLVM API Documentation

Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

FileUtilities.cpp

Go to the documentation of this file.
00001 //===- Support/FileUtilities.cpp - File System Utilities ------------------===//
00002 // 
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file was developed by the LLVM research group and is distributed under
00006 // the University of Illinois Open Source License. See LICENSE.TXT for details.
00007 // 
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file implements a family of utility functions which are useful for doing
00011 // various things with files.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "llvm/Support/FileUtilities.h"
00016 #include "llvm/Support/DataTypes.h"
00017 #include "llvm/Config/unistd.h"
00018 #include "llvm/Config/fcntl.h"
00019 #include "llvm/Config/sys/types.h"
00020 #include "llvm/Config/sys/stat.h"
00021 #include "llvm/Config/sys/mman.h"
00022 #include "llvm/Config/alloca.h"
00023 #include <cerrno>
00024 #include <cstdio>
00025 #include <fstream>
00026 #include <iostream>
00027 using namespace llvm;
00028 
00029 /// CheckMagic - Returns true IFF the file named FN begins with Magic. FN must
00030 /// name a readable file.
00031 ///
00032 bool llvm::CheckMagic(const std::string &FN, const std::string &Magic) {
00033   char *buf = (char*)alloca(1 + Magic.size());
00034   std::ifstream f(FN.c_str());
00035   f.read(buf, Magic.size());
00036   buf[Magic.size()] = '\0';
00037   return Magic == buf;
00038 }
00039 
00040 /// IsArchive - Returns true IFF the file named FN appears to be a "ar" library
00041 /// archive. The file named FN must exist.
00042 ///
00043 bool llvm::IsArchive(const std::string &FN) {
00044   // Inspect the beginning of the file to see if it contains the "ar"
00045   // library archive format magic string.
00046   return CheckMagic(FN, "!<arch>\012");
00047 }
00048 
00049 /// IsBytecode - Returns true IFF the file named FN appears to be an LLVM
00050 /// bytecode file. The file named FN must exist.
00051 ///
00052 bool llvm::IsBytecode(const std::string &FN) {
00053   // Inspect the beginning of the file to see if it contains the LLVM
00054   // bytecode format magic string.
00055   return CheckMagic(FN, "llvm") || CheckMagic(FN, "llvc");
00056 }
00057 
00058 /// IsSharedObject - Returns trus IFF the file named FN appears to be a shared
00059 /// object with an ELF header. The file named FN must exist.
00060 ///
00061 bool llvm::IsSharedObject(const std::string &FN) {
00062   // Inspect the beginning of the file to see if it contains the ELF shared
00063   // object magic string.
00064   static const char elfMagic[] = { 0x7f, 'E', 'L', 'F', '\0' };
00065   return CheckMagic(FN, elfMagic);
00066 }
00067 
00068 /// FileOpenable - Returns true IFF Filename names an existing regular
00069 /// file which we can successfully open.
00070 ///
00071 bool llvm::FileOpenable(const std::string &Filename) {
00072   struct stat s;
00073   if (stat (Filename.c_str (), &s) == -1)
00074     return false; // Cannot stat file
00075   if (!S_ISREG (s.st_mode))
00076     return false; // File is not a regular file
00077   std::ifstream FileStream (Filename.c_str ());
00078   if (!FileStream)
00079     return false; // File is not openable
00080   return true;
00081 }
00082 
00083 /// DiffFiles - Compare the two files specified, returning true if they are
00084 /// different or if there is a file error.  If you specify a string to fill in
00085 /// for the error option, it will set the string to an error message if an error
00086 /// occurs, allowing the caller to distinguish between a failed diff and a file
00087 /// system error.
00088 ///
00089 bool llvm::DiffFiles(const std::string &FileA, const std::string &FileB,
00090                      std::string *Error) {
00091   std::ifstream FileAStream(FileA.c_str());
00092   if (!FileAStream) {
00093     if (Error) *Error = "Couldn't open file '" + FileA + "'";
00094     return true;
00095   }
00096 
00097   std::ifstream FileBStream(FileB.c_str());
00098   if (!FileBStream) {
00099     if (Error) *Error = "Couldn't open file '" + FileB + "'";
00100     return true;
00101   }
00102 
00103   // Compare the two files...
00104   int C1, C2;
00105   do {
00106     C1 = FileAStream.get();
00107     C2 = FileBStream.get();
00108     if (C1 != C2) return true;
00109   } while (C1 != EOF);
00110 
00111   return false;
00112 }
00113 
00114 
00115 /// CopyFile - Copy the specified source file to the specified destination,
00116 /// overwriting destination if it exists.  This returns true on failure.
00117 ///
00118 bool llvm::CopyFile(const std::string &Dest, const std::string &Src) {
00119   FDHandle InFD(open(Src.c_str(), O_RDONLY));
00120   if (InFD == -1) return true;
00121 
00122   FileRemover FR(Dest);
00123 
00124   FDHandle OutFD(open(Dest.c_str(), O_WRONLY|O_CREAT, 0666));
00125   if (OutFD == -1) return true;
00126 
00127   char Buffer[16*1024];
00128   while (ssize_t Amt = read(InFD, Buffer, 16*1024)) {
00129     if (Amt == -1) {
00130       if (errno != EINTR) return true;  // Error reading the file.
00131     } else {
00132       char *BufPtr = Buffer;
00133       while (Amt) {
00134         ssize_t AmtWritten = write(OutFD, BufPtr, Amt);
00135         if (AmtWritten == -1) {
00136           if (errno != EINTR) return true;  // Error writing the file.
00137         } else {
00138           Amt -= AmtWritten;
00139           BufPtr += AmtWritten;
00140         }
00141       }
00142     }
00143   }
00144 
00145   FR.releaseFile();  // Success!
00146   return false;
00147 }
00148 
00149 
00150 /// MoveFileOverIfUpdated - If the file specified by New is different than Old,
00151 /// or if Old does not exist, move the New file over the Old file.  Otherwise,
00152 /// remove the New file.
00153 ///
00154 void llvm::MoveFileOverIfUpdated(const std::string &New,
00155                                  const std::string &Old) {
00156   if (DiffFiles(New, Old)) {
00157     if (std::rename(New.c_str(), Old.c_str()))
00158       std::cerr << "Error renaming '" << New << "' to '" << Old << "'!\n";
00159   } else {
00160     std::remove(New.c_str());
00161   }  
00162 }
00163 
00164 /// removeFile - Delete the specified file
00165 ///
00166 void llvm::removeFile(const std::string &Filename) {
00167   std::remove(Filename.c_str());
00168 }
00169 
00170 /// getUniqueFilename - Return a filename with the specified prefix.  If the
00171 /// file does not exist yet, return it, otherwise add a suffix to make it
00172 /// unique.
00173 ///
00174 std::string llvm::getUniqueFilename(const std::string &FilenameBase) {
00175   if (!std::ifstream(FilenameBase.c_str()))
00176     return FilenameBase;    // Couldn't open the file? Use it!
00177 
00178   // Create a pattern for mkstemp...
00179   char *FNBuffer = new char[FilenameBase.size()+8];
00180   strcpy(FNBuffer, FilenameBase.c_str());
00181   strcpy(FNBuffer+FilenameBase.size(), "-XXXXXX");
00182 
00183   // Agree on a temporary file name to use....
00184 #if defined(HAVE_MKSTEMP) && !defined(_MSC_VER)
00185   int TempFD;
00186   if ((TempFD = mkstemp(FNBuffer)) == -1) {
00187     // FIXME: this should return an emtpy string or something and allow the
00188     // caller to deal with the error!
00189     std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current "
00190         << " directory!\n";
00191     exit(1);
00192   }
00193 
00194   // We don't need to hold the temp file descriptor... we will trust that no one
00195   // will overwrite/delete the file while we are working on it...
00196   close(TempFD);
00197 #else
00198   // If we don't have mkstemp, use the old and obsolete mktemp function.
00199   if (mktemp(FNBuffer) == 0) {
00200     // FIXME: this should return an emtpy string or something and allow the
00201     // caller to deal with the error!
00202     std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current "
00203               << " directory!\n";
00204     exit(1);
00205   }
00206 #endif
00207 
00208   std::string Result(FNBuffer);
00209   delete[] FNBuffer;
00210   return Result;
00211 }
00212 
00213 static bool AddPermissionsBits (const std::string &Filename, int bits) {
00214   // Get the umask value from the operating system.  We want to use it
00215   // when changing the file's permissions. Since calling umask() sets
00216   // the umask and returns its old value, we must call it a second
00217   // time to reset it to the user's preference.
00218   int mask = umask(0777); // The arg. to umask is arbitrary.
00219   umask(mask);            // Restore the umask.
00220 
00221   // Get the file's current mode.
00222   struct stat st;
00223   if ((stat(Filename.c_str(), &st)) == -1)
00224     return false;
00225 
00226   // Change the file to have whichever permissions bits from 'bits'
00227   // that the umask would not disable.
00228   if ((chmod(Filename.c_str(), (st.st_mode | (bits & ~mask)))) == -1)
00229     return false;
00230 
00231   return true;
00232 }
00233 
00234 /// MakeFileExecutable - Make the file named Filename executable by
00235 /// setting whichever execute permissions bits the process's current
00236 /// umask would allow. Filename must name an existing file or
00237 /// directory.  Returns true on success, false on error.
00238 ///
00239 bool llvm::MakeFileExecutable(const std::string &Filename) {
00240   return AddPermissionsBits(Filename, 0111);
00241 }
00242 
00243 /// MakeFileReadable - Make the file named Filename readable by
00244 /// setting whichever read permissions bits the process's current
00245 /// umask would allow. Filename must name an existing file or
00246 /// directory.  Returns true on success, false on error.
00247 ///
00248 bool llvm::MakeFileReadable(const std::string &Filename) {
00249   return AddPermissionsBits(Filename, 0444);
00250 }
00251 
00252 /// getFileSize - Return the size of the specified file in bytes, or -1 if the
00253 /// file cannot be read or does not exist.
00254 long long llvm::getFileSize(const std::string &Filename) {
00255   struct stat StatBuf;
00256   if (stat(Filename.c_str(), &StatBuf) == -1)
00257     return -1;
00258   return StatBuf.st_size;  
00259 }
00260 
00261 /// getFileTimestamp - Get the last modified time for the specified file in an
00262 /// unspecified format.  This is useful to allow checking to see if a file was
00263 /// updated since that last time the timestampt was aquired.  If the file does
00264 /// not exist or there is an error getting the time-stamp, zero is returned.
00265 unsigned long long llvm::getFileTimestamp(const std::string &Filename) {
00266   struct stat StatBuf;
00267   if (stat(Filename.c_str(), &StatBuf) == -1)
00268     return 0;
00269   return StatBuf.st_mtime;  
00270 }
00271 
00272 /// ReadFileIntoAddressSpace - Attempt to map the specific file into the
00273 /// address space of the current process for reading.  If this succeeds,
00274 /// return the address of the buffer and the length of the file mapped.  On
00275 /// failure, return null.
00276 void *llvm::ReadFileIntoAddressSpace(const std::string &Filename, 
00277                                      unsigned &Length) {
00278 #if defined(HAVE_MMAP_FILE) && !defined(_MSC_VER)
00279   Length = (unsigned)getFileSize(Filename);
00280   if ((int)Length == -1) return 0;
00281 
00282   FDHandle FD(open(Filename.c_str(), O_RDONLY));
00283   if (FD == -1) return 0;
00284 
00285   // If the file has a length of zero, mmap might return a null pointer.  In 
00286   // this case, allocate a single byte of memory and return it instead.
00287   if (Length == 0)
00288     return malloc(1);
00289 
00290   // mmap in the file all at once...
00291   void *Buffer = (void*)mmap(0, Length, PROT_READ, MAP_PRIVATE, FD, 0);
00292 
00293   if (Buffer == (void*)MAP_FAILED)
00294     return 0;
00295 
00296   return Buffer;
00297 #else
00298   // FIXME: implement with read/write
00299 #error Unimplemented ReadFileIntoAddressSpace - need to use read/write.
00300   return 0;
00301 #endif
00302 }
00303 
00304 /// UnmapFileFromAddressSpace - Remove the specified file from the current
00305 /// address space.
00306 void llvm::UnmapFileFromAddressSpace(void *Buffer, unsigned Length) {
00307 #if defined(HAVE_MMAP_FILE) && !defined(_MSC_VER)
00308   if (Length)
00309     munmap((char*)Buffer, Length);
00310   else
00311     free(Buffer);  // Zero byte files are malloc(1)'s.
00312 #else
00313   free(Buffer);
00314 #endif
00315 }
00316 
00317 //===----------------------------------------------------------------------===//
00318 // FDHandle class implementation
00319 //
00320 
00321 FDHandle::~FDHandle() throw() {
00322   if (FD != -1) close(FD);
00323 }
00324 
00325 FDHandle &FDHandle::operator=(int fd) throw() {
00326   if (FD != -1) close(FD);
00327   FD = fd;
00328   return *this;
00329 }
00330