Start using CHECK-LABEL in some tests.
[oota-llvm.git] / tools / llvm-ar / llvm-ar.cpp
index 792b46bded0e46cb63fa7b18654caad8f8001aec..fd6841c9f16de57b8555329bcde3437fe4283fcb 100644 (file)
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cstdlib>
-#include <fstream>
+#include <fcntl.h>
 #include <memory>
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
 using namespace llvm;
 
 // Option for compatibility with AIX, not used but must allow it to be present.
@@ -57,26 +64,20 @@ static cl::extrahelp MoreHelp(
   "\nMODIFIERS (operation specific):\n"
   "  [a] - put file(s) after [relpos]\n"
   "  [b] - put file(s) before [relpos] (same as [i])\n"
-  "  [f] - truncate inserted file names\n"
   "  [i] - put file(s) before [relpos] (same as [b])\n"
-  "  [k] - always print bitcode files (default is to skip them)\n"
   "  [N] - use instance [count] of name\n"
   "  [o] - preserve original dates\n"
-  "  [P] - use full path names when matching\n"
-  "  [R] - recurse through directories when inserting\n"
   "  [s] - create an archive index (cf. ranlib)\n"
   "  [S] - do not build a symbol table\n"
   "  [u] - update only files newer than archive contents\n"
   "\nMODIFIERS (generic):\n"
   "  [c] - do not warn if the library had to be created\n"
   "  [v] - be verbose about actions taken\n"
-  "  [V] - be *really* verbose about actions taken\n"
 );
 
 // This enumeration delineates the kinds of operations on an archive
 // that are permitted.
 enum ArchiveOperation {
-  NoOperation,      ///< An operation hasn't been specified
   Print,            ///< Print the contents of the archive
   Delete,           ///< Delete the specified members
   Move,             ///< Move members to end or as given by {a,b,i} modifiers
@@ -90,16 +91,10 @@ enum ArchiveOperation {
 bool AddAfter = false;           ///< 'a' modifier
 bool AddBefore = false;          ///< 'b' modifier
 bool Create = false;             ///< 'c' modifier
-bool TruncateNames = false;      ///< 'f' modifier
-bool InsertBefore = false;       ///< 'i' modifier
-bool DontSkipBitcode = false;    ///< 'k' modifier
-bool UseCount = false;           ///< 'N' modifier
 bool OriginalDates = false;      ///< 'o' modifier
-bool FullPath = false;           ///< 'P' modifier
 bool SymTable = true;            ///< 's' & 'S' modifiers
 bool OnlyUpdate = false;         ///< 'u' modifier
 bool Verbose = false;            ///< 'v' modifier
-bool ReallyVerbose = false;      ///< 'V' modifier
 
 // Relative Positional Argument (for insert/move). This variable holds
 // the name of the archive member to which the 'a', 'b' or 'i' modifier
@@ -107,10 +102,6 @@ bool ReallyVerbose = false;      ///< 'V' modifier
 // one variable.
 std::string RelPos;
 
-// Select which of multiple entries in the archive with the same name should be
-// used (specified with -N) for the delete and extract operations.
-int Count = 1;
-
 // This variable holds the name of the archive file as given on the
 // command line.
 std::string ArchiveName;
@@ -121,7 +112,7 @@ std::vector<std::string> Members;
 
 // This variable holds the (possibly expanded) list of path objects that
 // correspond to files we will
-std::set<sys::Path> Paths;
+std::set<std::string> Paths;
 
 // The Archive object to which all the editing operations will be sent.
 Archive* TheArchive = 0;
@@ -157,20 +148,6 @@ void getRelPos() {
   RestOfArgs.erase(RestOfArgs.begin());
 }
 
-// getCount - Extract the [count] argument associated with the N modifier
-// from the command line and check its value.
-void getCount() {
-  if(RestOfArgs.size() == 0)
-    show_help("Expected [count] value with N modifier");
-
-  Count = atoi(RestOfArgs[0].c_str());
-  RestOfArgs.erase(RestOfArgs.begin());
-
-  // Non-positive counts are not allowed
-  if (Count < 1)
-    show_help("Invalid [count] value (not a positive integer)");
-}
-
 // getArchive - Get the archive file name from the command line
 void getArchive() {
   if(RestOfArgs.size() == 0)
@@ -200,7 +177,7 @@ ArchiveOperation parseCommandLine() {
   unsigned NumPositional = 0;
 
   // Keep track of which operation was requested
-  ArchiveOperation Operation = NoOperation;
+  ArchiveOperation Operation;
 
   for(unsigned i=0; i<Options.size(); ++i) {
     switch(Options[i]) {
@@ -212,16 +189,12 @@ ArchiveOperation parseCommandLine() {
     case 't': ++NumOperations; Operation = DisplayTable; break;
     case 'x': ++NumOperations; Operation = Extract; break;
     case 'c': Create = true; break;
-    case 'f': TruncateNames = true; break;
-    case 'k': DontSkipBitcode = true; break;
     case 'l': /* accepted but unused */ break;
     case 'o': OriginalDates = true; break;
-    case 'P': FullPath = true; break;
-    case 's': SymTable = true; break;
-    case 'S': SymTable = false; break;
+    case 's': break; // Ignore for now.
+    case 'S': break; // Ignore for now.
     case 'u': OnlyUpdate = true; break;
     case 'v': Verbose = true; break;
-    case 'V': Verbose = ReallyVerbose = true; break;
     case 'a':
       getRelPos();
       AddAfter = true;
@@ -234,13 +207,9 @@ ArchiveOperation parseCommandLine() {
       break;
     case 'i':
       getRelPos();
-      InsertBefore = true;
+      AddBefore = true;
       NumPositional++;
       break;
-    case 'N':
-      getCount();
-      UseCount = true;
-      break;
     default:
       cl::PrintHelpMessage();
     }
@@ -261,20 +230,15 @@ ArchiveOperation parseCommandLine() {
     show_help("Only one operation may be specified");
   if (NumPositional > 1)
     show_help("You may only specify one of a, b, and i modifiers");
-  if (AddAfter || AddBefore || InsertBefore) {
+  if (AddAfter || AddBefore) {
     if (Operation != Move && Operation != ReplaceOrInsert)
       show_help("The 'a', 'b' and 'i' modifiers can only be specified with "
             "the 'm' or 'r' operations");
   }
   if (OriginalDates && Operation != Extract)
     show_help("The 'o' modifier is only applicable to the 'x' operation");
-  if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert)
-    show_help("The 'f' modifier is only applicable to the 'q' and 'r' "
-              "operations");
   if (OnlyUpdate && Operation != ReplaceOrInsert)
     show_help("The 'u' modifier is only applicable to the 'r' operation");
-  if (Count > 1 && Members.size() > 1)
-    show_help("Only one member name may be specified with the 'N' modifier");
 
   // Return the parsed operation to the caller
   return Operation;
@@ -285,20 +249,14 @@ ArchiveOperation parseCommandLine() {
 // the operations that add/replace files to the archive ('q' and 'r')
 bool buildPaths(bool checkExistence, std::string* ErrMsg) {
   for (unsigned i = 0; i < Members.size(); i++) {
-    sys::Path aPath;
-    if (!aPath.set(Members[i]))
-      fail(std::string("File member name invalid: ") + Members[i]);
+    std::string aPath = Members[i];
     if (checkExistence) {
-      bool Exists;
-      if (sys::fs::exists(aPath.str(), Exists) || !Exists)
-        fail(std::string("File does not exist: ") + Members[i]);
-      std::string Err;
-      sys::PathWithStatus PwS(aPath);
-      const sys::FileStatus *si = PwS.getFileStatus(false, &Err);
-      if (!si)
-        fail(Err);
-      if (si->isDir)
-        fail(aPath.str() + " Is a directory");
+      bool IsDirectory;
+      error_code EC = sys::fs::is_directory(aPath, IsDirectory);
+      if (EC)
+        fail(aPath + ": " + EC.message());
+      if (IsDirectory)
+        fail(aPath + " Is a directory");
 
       Paths.insert(aPath);
     } else {
@@ -308,17 +266,6 @@ bool buildPaths(bool checkExistence, std::string* ErrMsg) {
   return false;
 }
 
-// printSymbolTable - print out the archive's symbol table.
-void printSymbolTable() {
-  outs() << "\nArchive Symbol Table:\n";
-  const Archive::SymTabType& symtab = TheArchive->getSymbolTable();
-  for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end();
-       I != E; ++I ) {
-    unsigned offset = TheArchive->getFirstFileOffset() + I->second;
-    outs() << " " << format("%9u", offset) << "\t" << I->first <<"\n";
-  }
-}
-
 // doPrint - Implements the 'p' operation. This function traverses the archive
 // looking for members that match the path list. It is careful to uncompress
 // things that should be and to skip bitcode files unless the 'k' modifier was
@@ -326,27 +273,21 @@ void printSymbolTable() {
 bool doPrint(std::string* ErrMsg) {
   if (buildPaths(false, ErrMsg))
     return true;
-  unsigned countDown = Count;
   for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
        I != E; ++I ) {
     if (Paths.empty() ||
         (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) {
-      if (countDown == 1) {
-        const char* data = reinterpret_cast<const char*>(I->getData());
+      const char *data = reinterpret_cast<const char *>(I->getData());
 
-        // Skip things that don't make sense to print
-        if (I->isSVR4SymbolTable() ||
-            I->isBSD4SymbolTable() || (!DontSkipBitcode && I->isBitcode()))
-          continue;
+      // Skip things that don't make sense to print
+      if (I->isSVR4SymbolTable() || I->isBSD4SymbolTable())
+        continue;
 
-        if (Verbose)
-          outs() << "Printing " << I->getPath().str() << "\n";
+      if (Verbose)
+        outs() << "Printing " << I->getPath().str() << "\n";
 
-        unsigned len = I->getSize();
-        outs().write(data, len);
-      } else {
-        countDown--;
-      }
+      unsigned len = I->getSize();
+      outs().write(data, len);
     }
   }
   return false;
@@ -383,28 +324,21 @@ doDisplayTable(std::string* ErrMsg) {
     if (Paths.empty() ||
         (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) {
       if (Verbose) {
-        // FIXME: Output should be this format:
-        // Zrw-r--r--  500/ 500    525 Nov  8 17:42 2004 Makefile
-        if (I->isBitcode())
-          outs() << "b";
-        else
-          outs() << " ";
         unsigned mode = I->getMode();
         printMode((mode >> 6) & 007);
         printMode((mode >> 3) & 007);
         printMode(mode & 007);
-        outs() << " " << format("%4u", I->getUser());
-        outs() << "/" << format("%4u", I->getGroup());
-        outs() << " " << format("%8u", I->getSize());
-        outs() << " " << format("%20s", I->getModTime().str().substr(4).c_str());
+        outs() << ' ' << I->getUser();
+        outs() << "/" << I->getGroup();
+        outs() << ' ' << format("%6llu", I->getSize());
+        sys::TimeValue ModTime = I->getModTime();
+        outs() << " " << ModTime.str();
         outs() << " " << I->getPath().str() << "\n";
       } else {
         outs() << I->getPath().str() << "\n";
       }
     }
   }
-  if (ReallyVerbose)
-    printSymbolTable();
   return false;
 }
 
@@ -419,31 +353,40 @@ doExtract(std::string* ErrMsg) {
     if (Paths.empty() ||
         (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) {
 
-      // Make sure the intervening directories are created
-      if (I->hasPath()) {
-        sys::Path dirs(I->getPath());
-        dirs.eraseComponent();
-        if (dirs.createDirectoryOnDisk(/*create_parents=*/true, ErrMsg))
-          return true;
-      }
-
       // Open up a file stream for writing
-      std::ios::openmode io_mode = std::ios::out | std::ios::trunc |
-                                   std::ios::binary;
-      std::ofstream file(I->getPath().c_str(), io_mode);
+      int OpenFlags = O_TRUNC | O_WRONLY | O_CREAT;
+#ifdef O_BINARY
+      OpenFlags |= O_BINARY;
+#endif
 
-      // Get the data and its length
-      const char* data = reinterpret_cast<const char*>(I->getData());
-      unsigned len = I->getSize();
+      // Retain the original mode.
+      sys::fs::perms Mode = sys::fs::perms(I->getMode());
+
+      int FD = open(I->getPath().str().c_str(), OpenFlags, Mode);
+      if (FD < 0)
+        return true;
+
+      {
+        raw_fd_ostream file(FD, false);
+
+        // Get the data and its length
+        const char* data = reinterpret_cast<const char*>(I->getData());
+        unsigned len = I->getSize();
 
-      // Write the data.
-      file.write(data,len);
-      file.close();
+        // Write the data.
+        file.write(data, len);
+      }
 
       // If we're supposed to retain the original modification times, etc. do so
       // now.
-      if (OriginalDates)
-        I->getPath().setStatusInfoOnDisk(I->getFileStatus());
+      if (OriginalDates) {
+        error_code EC =
+            sys::fs::setLastModificationAndAccessTime(FD, I->getModTime());
+        if (EC)
+          fail(EC.message());
+      }
+      if (close(FD))
+        return true;
     }
   }
   return false;
@@ -459,26 +402,20 @@ doDelete(std::string* ErrMsg) {
     return true;
   if (Paths.empty())
     return false;
-  unsigned countDown = Count;
   for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
        I != E; ) {
     if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) {
-      if (countDown == 1) {
-        Archive::iterator J = I;
-        ++I;
-        TheArchive->erase(J);
-      } else
-        countDown--;
+      Archive::iterator J = I;
+      ++I;
+      TheArchive->erase(J);
     } else {
       ++I;
     }
   }
 
   // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg))
+  if (TheArchive->writeToDisk(ErrMsg))
     return true;
-  if (ReallyVerbose)
-    printSymbolTable();
   return false;
 }
 
@@ -498,7 +435,7 @@ doMove(std::string* ErrMsg) {
   // However, if the relative positioning modifiers were used, we need to scan
   // the archive to find the member in question. If we don't find it, its no
   // crime, we just move to the end.
-  if (AddBefore || InsertBefore || AddAfter) {
+  if (AddBefore || AddAfter) {
     for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end();
          I != E; ++I ) {
       if (RelPos == I->getPath().str()) {
@@ -514,14 +451,14 @@ doMove(std::string* ErrMsg) {
   }
 
   // Keep a list of the paths remaining to be moved
-  std::set<sys::Path> remaining(Paths);
+  std::set<std::string> remaining(Paths);
 
   // Scan the archive again, this time looking for the members to move to the
   // moveto_spot.
   for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end();
        I != E && !remaining.empty(); ++I ) {
-    std::set<sys::Path>::iterator found =
-      std::find(remaining.begin(),remaining.end(),I->getPath());
+    std::set<std::string>::iterator found =
+      std::find(remaining.begin(),remaining.end(), I->getPath());
     if (found != remaining.end()) {
       if (I != moveto_spot)
         TheArchive->splice(moveto_spot,*TheArchive,I);
@@ -530,10 +467,8 @@ doMove(std::string* ErrMsg) {
   }
 
   // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg))
+  if (TheArchive->writeToDisk(ErrMsg))
     return true;
-  if (ReallyVerbose)
-    printSymbolTable();
   return false;
 }
 
@@ -548,17 +483,15 @@ doQuickAppend(std::string* ErrMsg) {
     return false;
 
   // Append them quickly.
-  for (std::set<sys::Path>::iterator PI = Paths.begin(), PE = Paths.end();
+  for (std::set<std::string>::iterator PI = Paths.begin(), PE = Paths.end();
        PI != PE; ++PI) {
-    if (TheArchive->addFileBefore(*PI,TheArchive->end(),ErrMsg))
+    if (TheArchive->addFileBefore(*PI, TheArchive->end(), ErrMsg))
       return true;
   }
 
   // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg))
+  if (TheArchive->writeToDisk(ErrMsg))
     return true;
-  if (ReallyVerbose)
-    printSymbolTable();
   return false;
 }
 
@@ -574,7 +507,7 @@ doReplaceOrInsert(std::string* ErrMsg) {
     return false;
 
   // Keep track of the paths that remain to be inserted.
-  std::set<sys::Path> remaining(Paths);
+  std::set<std::string> remaining(Paths);
 
   // Default the insertion spot to the end of the archive
   Archive::iterator insert_spot = TheArchive->end();
@@ -586,22 +519,10 @@ doReplaceOrInsert(std::string* ErrMsg) {
     // Determine if this archive member matches one of the paths we're trying
     // to replace.
 
-    std::set<sys::Path>::iterator found = remaining.end();
-    for (std::set<sys::Path>::iterator RI = remaining.begin(),
+    std::set<std::string>::iterator found = remaining.end();
+    for (std::set<std::string>::iterator RI = remaining.begin(),
          RE = remaining.end(); RI != RE; ++RI ) {
-      std::string compare(RI->str());
-      if (TruncateNames && compare.length() > 15) {
-        const char* nm = compare.c_str();
-        unsigned len = compare.length();
-        size_t slashpos = compare.rfind('/');
-        if (slashpos != std::string::npos) {
-          nm += slashpos + 1;
-          len -= slashpos +1;
-        }
-        if (len > 15)
-          len = 15;
-        compare.assign(nm,len);
-      }
+      std::string compare(sys::path::filename(*RI));
       if (compare == I->getPath().str()) {
         found = RI;
         break;
@@ -609,15 +530,14 @@ doReplaceOrInsert(std::string* ErrMsg) {
     }
 
     if (found != remaining.end()) {
-      std::string Err;
-      sys::PathWithStatus PwS(*found);
-      const sys::FileStatus *si = PwS.getFileStatus(false, &Err);
-      if (!si)
+      sys::fs::file_status Status;
+      error_code EC = sys::fs::status(*found, Status);
+      if (EC)
         return true;
-      if (!si->isDir) {
+      if (!sys::fs::is_directory(Status)) {
         if (OnlyUpdate) {
           // Replace the item only if it is newer.
-          if (si->modTime > I->getModTime())
+          if (Status.getLastModificationTime() > I->getModTime())
             if (I->replaceWith(*found, ErrMsg))
               return true;
         } else {
@@ -634,7 +554,7 @@ doReplaceOrInsert(std::string* ErrMsg) {
     }
 
     // Determine if this is the place where we should insert
-    if ((AddBefore || InsertBefore) && RelPos == I->getPath().str())
+    if (AddBefore && RelPos == I->getPath().str())
       insert_spot = I;
     else if (AddAfter && RelPos == I->getPath().str()) {
       insert_spot = I;
@@ -645,21 +565,36 @@ doReplaceOrInsert(std::string* ErrMsg) {
   // If we didn't replace all the members, some will remain and need to be
   // inserted at the previously computed insert-spot.
   if (!remaining.empty()) {
-    for (std::set<sys::Path>::iterator PI = remaining.begin(),
+    for (std::set<std::string>::iterator PI = remaining.begin(),
          PE = remaining.end(); PI != PE; ++PI) {
-      if (TheArchive->addFileBefore(*PI,insert_spot, ErrMsg))
+      if (TheArchive->addFileBefore(*PI, insert_spot, ErrMsg))
         return true;
     }
   }
 
   // We're done editting, reconstruct the archive.
-  if (TheArchive->writeToDisk(SymTable,TruncateNames,ErrMsg))
+  if (TheArchive->writeToDisk(ErrMsg))
     return true;
-  if (ReallyVerbose)
-    printSymbolTable();
   return false;
 }
 
+bool shouldCreateArchive(ArchiveOperation Op) {
+  switch (Op) {
+  case Print:
+  case Delete:
+  case Move:
+  case DisplayTable:
+  case Extract:
+    return false;
+
+  case QuickAppend:
+  case ReplaceOrInsert:
+    return true;
+  }
+
+  llvm_unreachable("Missing entry in covered switch.");
+}
+
 // main - main program for llvm-ar .. see comments in the code
 int main(int argc, char **argv) {
   program_name = argv[0];
@@ -682,26 +617,20 @@ int main(int argc, char **argv) {
   // can't handle the grouped positional parameters without a dash.
   ArchiveOperation Operation = parseCommandLine();
 
-  // Check the path name of the archive
-  sys::Path ArchivePath;
-  if (!ArchivePath.set(ArchiveName)) {
-    errs() << argv[0] << ": Archive name invalid: " << ArchiveName << "\n";
-    return 1;
-  }
-
   // Create or open the archive object.
-  bool Exists;
-  if (llvm::sys::fs::exists(ArchivePath.str(), Exists) || !Exists) {
+  if (shouldCreateArchive(Operation) && !llvm::sys::fs::exists(ArchiveName)) {
     // Produce a warning if we should and we're creating the archive
     if (!Create)
-      errs() << argv[0] << ": creating " << ArchivePath.str() << "\n";
-    TheArchive = Archive::CreateEmpty(ArchivePath, Context);
+      errs() << argv[0] << ": creating " << ArchiveName << "\n";
+    TheArchive = Archive::CreateEmpty(ArchiveName, Context);
     TheArchive->writeToDisk();
-  } else {
+  }
+
+  if (!TheArchive) {
     std::string Error;
-    TheArchive = Archive::OpenAndLoad(ArchivePath, Context, &Error);
+    TheArchive = Archive::OpenAndLoad(ArchiveName, Context, &Error);
     if (TheArchive == 0) {
-      errs() << argv[0] << ": error loading '" << ArchivePath.str() << "': "
+      errs() << argv[0] << ": error loading '" << ArchiveName << "': "
              << Error << "!\n";
       return 1;
     }
@@ -721,9 +650,6 @@ int main(int argc, char **argv) {
     case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break;
     case DisplayTable:    haveError = doDisplayTable(&ErrMsg); break;
     case Extract:         haveError = doExtract(&ErrMsg); break;
-    case NoOperation:
-      errs() << argv[0] << ": No operation was selected.\n";
-      break;
   }
   if (haveError) {
     errs() << argv[0] << ": " << ErrMsg << "\n";