mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-23 15:29:51 +00:00
3ed469ccd7
Turn on -Wunused and -Wno-unused-parameter. Clean up most of the resulting fall out by removing unused variables. Remaining warnings have to do with unused functions (I didn't want to delete code without review) and unused variables in generated code. Maintainers should clean up the remaining issues when they see them. All changes pass DejaGnu tests and Olden. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@31380 91177308-0d34-0410-b5e6-96231b3b80d8
784 lines
27 KiB
C++
784 lines
27 KiB
C++
//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file was developed by the LLVM research group and is distributed under
|
|
// the University of Illinois Open Source License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Builds up (relatively) standard unix archive files (.a) containing LLVM
|
|
// bytecode or other files.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Module.h"
|
|
#include "llvm/Bytecode/Archive.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Compressor.h"
|
|
#include "llvm/System/Signals.h"
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
#include <memory>
|
|
|
|
using namespace llvm;
|
|
|
|
// Option for compatibility with ASIX, not used but must allow it to be present.
|
|
static cl::opt<bool>
|
|
X32Option ("X32_64", cl::Hidden,
|
|
cl::desc("Ignored option for compatibility with AIX"));
|
|
|
|
// llvm-ar operation code and modifier flags. This must come first.
|
|
static cl::opt<std::string>
|
|
Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]..."));
|
|
|
|
// llvm-ar remaining positional arguments.
|
|
static cl::list<std::string>
|
|
RestOfArgs(cl::Positional, cl::OneOrMore,
|
|
cl::desc("[relpos] [count] <archive-file> [members]..."));
|
|
|
|
// MoreHelp - Provide additional help output explaining the operations and
|
|
// modifiers of llvm-ar. This object instructs the CommandLine library
|
|
// to print the text of the constructor when the --help option is given.
|
|
static cl::extrahelp MoreHelp(
|
|
"\nOPERATIONS:\n"
|
|
" d[NsS] - delete file(s) from the archive\n"
|
|
" m[abiSs] - move file(s) in the archive\n"
|
|
" p[kN] - print file(s) found in the archive\n"
|
|
" q[ufsS] - quick append file(s) to the archive\n"
|
|
" r[abfiuzRsS] - replace or insert file(s) into the archive\n"
|
|
" t - display contents of archive\n"
|
|
" x[No] - extract file(s) from the archive\n"
|
|
"\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 bytecode 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"
|
|
" [z] - compress files before inserting/extracting\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
|
|
QuickAppend, ///< Quickly append to end of archive
|
|
ReplaceOrInsert, ///< Replace or Insert members
|
|
DisplayTable, ///< Display the table of contents
|
|
Extract ///< Extract files back to file system
|
|
};
|
|
|
|
// Modifiers to follow operation to vary behavior
|
|
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 DontSkipBytecode = false; ///< 'k' modifier
|
|
bool UseCount = false; ///< 'N' modifier
|
|
bool OriginalDates = false; ///< 'o' modifier
|
|
bool FullPath = false; ///< 'P' modifier
|
|
bool RecurseDirectories = false; ///< 'R' modifier
|
|
bool SymTable = true; ///< 's' & 'S' modifiers
|
|
bool OnlyUpdate = false; ///< 'u' modifier
|
|
bool Verbose = false; ///< 'v' modifier
|
|
bool ReallyVerbose = false; ///< 'V' modifier
|
|
bool Compression = false; ///< 'z' modifier
|
|
|
|
// Relative Positional Argument (for insert/move). This variable holds
|
|
// the name of the archive member to which the 'a', 'b' or 'i' modifier
|
|
// refers. Only one of 'a', 'b' or 'i' can be specified so we only need
|
|
// 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;
|
|
|
|
// This variable holds the list of member files to proecess, as given
|
|
// on the command line.
|
|
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;
|
|
|
|
// The Archive object to which all the editing operations will be sent.
|
|
Archive* TheArchive = 0;
|
|
|
|
// getRelPos - Extract the member filename from the command line for
|
|
// the [relpos] argument associated with a, b, and i modifiers
|
|
void getRelPos() {
|
|
if(RestOfArgs.size() > 0) {
|
|
RelPos = RestOfArgs[0];
|
|
RestOfArgs.erase(RestOfArgs.begin());
|
|
}
|
|
else
|
|
throw "Expected [relpos] for a, b, or i modifier";
|
|
}
|
|
|
|
// getCount - Extract the [count] argument associated with the N modifier
|
|
// from the command line and check its value.
|
|
void getCount() {
|
|
if(RestOfArgs.size() > 0) {
|
|
Count = atoi(RestOfArgs[0].c_str());
|
|
RestOfArgs.erase(RestOfArgs.begin());
|
|
}
|
|
else
|
|
throw "Expected [count] value with N modifier";
|
|
|
|
// Non-positive counts are not allowed
|
|
if (Count < 1)
|
|
throw "Invalid [count] value (not a positive integer)";
|
|
}
|
|
|
|
// getArchive - Get the archive file name from the command line
|
|
void getArchive() {
|
|
if(RestOfArgs.size() > 0) {
|
|
ArchiveName = RestOfArgs[0];
|
|
RestOfArgs.erase(RestOfArgs.begin());
|
|
}
|
|
else
|
|
throw "An archive name must be specified.";
|
|
}
|
|
|
|
// getMembers - Copy over remaining items in RestOfArgs to our Members vector
|
|
// This is just for clarity.
|
|
void getMembers() {
|
|
if(RestOfArgs.size() > 0)
|
|
Members = std::vector<std::string>(RestOfArgs);
|
|
}
|
|
|
|
// parseCommandLine - Parse the command line options as presented and return the
|
|
// operation specified. Process all modifiers and check to make sure that
|
|
// constraints on modifier/operation pairs have not been violated.
|
|
ArchiveOperation parseCommandLine() {
|
|
|
|
// Keep track of number of operations. We can only specify one
|
|
// per execution.
|
|
unsigned NumOperations = 0;
|
|
|
|
// Keep track of the number of positional modifiers (a,b,i). Only
|
|
// one can be specified.
|
|
unsigned NumPositional = 0;
|
|
|
|
// Keep track of which operation was requested
|
|
ArchiveOperation Operation = NoOperation;
|
|
|
|
for(unsigned i=0; i<Options.size(); ++i) {
|
|
switch(Options[i]) {
|
|
case 'd': ++NumOperations; Operation = Delete; break;
|
|
case 'm': ++NumOperations; Operation = Move ; break;
|
|
case 'p': ++NumOperations; Operation = Print; break;
|
|
case 'r': ++NumOperations; Operation = ReplaceOrInsert; break;
|
|
case 't': ++NumOperations; Operation = DisplayTable; break;
|
|
case 'x': ++NumOperations; Operation = Extract; break;
|
|
case 'c': Create = true; break;
|
|
case 'f': TruncateNames = true; break;
|
|
case 'k': DontSkipBytecode = true; break;
|
|
case 'l': /* accepted but unused */ break;
|
|
case 'o': OriginalDates = true; break;
|
|
case 'P': FullPath = true; break;
|
|
case 'R': RecurseDirectories = true; break;
|
|
case 's': SymTable = true; break;
|
|
case 'S': SymTable = false; break;
|
|
case 'u': OnlyUpdate = true; break;
|
|
case 'v': Verbose = true; break;
|
|
case 'V': Verbose = ReallyVerbose = true; break;
|
|
case 'z': Compression = true; break;
|
|
case 'a':
|
|
getRelPos();
|
|
AddAfter = true;
|
|
NumPositional++;
|
|
break;
|
|
case 'b':
|
|
getRelPos();
|
|
AddBefore = true;
|
|
NumPositional++;
|
|
break;
|
|
case 'i':
|
|
getRelPos();
|
|
InsertBefore = true;
|
|
NumPositional++;
|
|
break;
|
|
case 'N':
|
|
getCount();
|
|
UseCount = true;
|
|
break;
|
|
default:
|
|
cl::PrintHelpMessage();
|
|
}
|
|
}
|
|
|
|
// At this point, the next thing on the command line must be
|
|
// the archive name.
|
|
getArchive();
|
|
|
|
// Everything on the command line at this point is a member.
|
|
getMembers();
|
|
|
|
// Perform various checks on the operation/modifier specification
|
|
// to make sure we are dealing with a legal request.
|
|
if (NumOperations == 0)
|
|
throw "You must specify at least one of the operations";
|
|
if (NumOperations > 1)
|
|
throw "Only one operation may be specified";
|
|
if (NumPositional > 1)
|
|
throw "You may only specify one of a, b, and i modifiers";
|
|
if (AddAfter || AddBefore || InsertBefore)
|
|
if (Operation != Move && Operation != ReplaceOrInsert)
|
|
throw "The 'a', 'b' and 'i' modifiers can only be specified with "
|
|
"the 'm' or 'r' operations";
|
|
if (RecurseDirectories && Operation != ReplaceOrInsert)
|
|
throw "The 'R' modifiers is only applicabe to the 'r' operation";
|
|
if (OriginalDates && Operation != Extract)
|
|
throw "The 'o' modifier is only applicable to the 'x' operation";
|
|
if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert)
|
|
throw "The 'f' modifier is only applicable to the 'q' and 'r' operations";
|
|
if (OnlyUpdate && Operation != ReplaceOrInsert)
|
|
throw "The 'u' modifier is only applicable to the 'r' operation";
|
|
if (Compression && Operation!=ReplaceOrInsert && Operation!=Extract)
|
|
throw "The 'z' modifier is only applicable to the 'r' and 'x' operations";
|
|
if (Count > 1 && Members.size() > 1)
|
|
throw "Only one member name may be specified with the 'N' modifier";
|
|
|
|
// Return the parsed operation to the caller
|
|
return Operation;
|
|
}
|
|
|
|
// recurseDirectories - Implements the "R" modifier. This function scans through
|
|
// the Paths vector (built by buildPaths, below) and replaces any directories it
|
|
// finds with all the files in that directory (recursively). It uses the
|
|
// sys::Path::getDirectoryContent method to perform the actual directory scans.
|
|
bool
|
|
recurseDirectories(const sys::Path& path,
|
|
std::set<sys::Path>& result, std::string* ErrMsg) {
|
|
result.clear();
|
|
if (RecurseDirectories) {
|
|
std::set<sys::Path> content;
|
|
if (path.getDirectoryContents(content, ErrMsg))
|
|
return true;
|
|
|
|
for (std::set<sys::Path>::iterator I = content.begin(), E = content.end();
|
|
I != E; ++I) {
|
|
// Make sure it exists and is a directory
|
|
sys::FileStatus Status;
|
|
if (I->getFileStatus(Status)) {
|
|
if (Status.isDir) {
|
|
std::set<sys::Path> moreResults;
|
|
if (recurseDirectories(*I, moreResults, ErrMsg))
|
|
return true;
|
|
result.insert(moreResults.begin(), moreResults.end());
|
|
} else {
|
|
result.insert(*I);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// buildPaths - Convert the strings in the Members vector to sys::Path objects
|
|
// and make sure they are valid and exist exist. This check is only needed for
|
|
// 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]))
|
|
throw std::string("File member name invalid: ") + Members[i];
|
|
if (checkExistence) {
|
|
if (!aPath.exists())
|
|
throw std::string("File does not exist: ") + Members[i];
|
|
sys::FileStatus si;
|
|
std::string Err;
|
|
if (aPath.getFileStatus(si, &Err))
|
|
throw Err;
|
|
if (si.isDir) {
|
|
std::set<sys::Path> dirpaths;
|
|
if (recurseDirectories(aPath, dirpaths, ErrMsg))
|
|
return true;
|
|
Paths.insert(dirpaths.begin(),dirpaths.end());
|
|
} else {
|
|
Paths.insert(aPath);
|
|
}
|
|
} else {
|
|
Paths.insert(aPath);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// printSymbolTable - print out the archive's symbol table.
|
|
void printSymbolTable() {
|
|
std::cout << "\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;
|
|
std::cout << " " << std::setw(9) << 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 bytecode files unless the 'k' modifier was
|
|
// given.
|
|
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());
|
|
|
|
// Skip things that don't make sense to print
|
|
if (I->isLLVMSymbolTable() || I->isSVR4SymbolTable() ||
|
|
I->isBSD4SymbolTable() || (!DontSkipBytecode &&
|
|
(I->isBytecode() || I->isCompressedBytecode())))
|
|
continue;
|
|
|
|
if (Verbose)
|
|
std::cout << "Printing " << I->getPath().toString() << "\n";
|
|
|
|
if (I->isCompressedBytecode())
|
|
Compressor::decompressToStream(data+4,I->getSize()-4,std::cout);
|
|
else if (I->isCompressed()) {
|
|
Compressor::decompressToStream(data,I->getSize(),std::cout);
|
|
} else {
|
|
unsigned len = I->getSize();
|
|
std::cout.write(data, len);
|
|
}
|
|
} else {
|
|
countDown--;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// putMode - utility function for printing out the file mode when the 't'
|
|
// operation is in verbose mode.
|
|
void
|
|
printMode(unsigned mode) {
|
|
if (mode & 004)
|
|
std::cout << "r";
|
|
else
|
|
std::cout << "-";
|
|
if (mode & 002)
|
|
std::cout << "w";
|
|
else
|
|
std::cout << "-";
|
|
if (mode & 001)
|
|
std::cout << "x";
|
|
else
|
|
std::cout << "-";
|
|
}
|
|
|
|
// doDisplayTable - Implement the 't' operation. This function prints out just
|
|
// the file names of each of the members. However, if verbose mode is requested
|
|
// ('v' modifier) then the file type, permission mode, user, group, size, and
|
|
// modification time are also printed.
|
|
bool
|
|
doDisplayTable(std::string* ErrMsg) {
|
|
if (buildPaths(false, ErrMsg))
|
|
return true;
|
|
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 (Verbose) {
|
|
// FIXME: Output should be this format:
|
|
// Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile
|
|
if (I->isBytecode())
|
|
std::cout << "b";
|
|
else if (I->isCompressedBytecode())
|
|
std::cout << "B";
|
|
else if (I->isCompressed())
|
|
std::cout << "Z";
|
|
else
|
|
std::cout << " ";
|
|
unsigned mode = I->getMode();
|
|
printMode((mode >> 6) & 007);
|
|
printMode((mode >> 3) & 007);
|
|
printMode(mode & 007);
|
|
std::cout << " " << std::setw(4) << I->getUser();
|
|
std::cout << "/" << std::setw(4) << I->getGroup();
|
|
std::cout << " " << std::setw(8) << I->getSize();
|
|
std::cout << " " << std::setw(20) <<
|
|
I->getModTime().toString().substr(4);
|
|
std::cout << " " << I->getPath().toString() << "\n";
|
|
} else {
|
|
std::cout << I->getPath().toString() << "\n";
|
|
}
|
|
}
|
|
}
|
|
if (ReallyVerbose)
|
|
printSymbolTable();
|
|
return false;
|
|
}
|
|
|
|
// doExtract - Implement the 'x' operation. This function extracts files back to
|
|
// the file system, making sure to uncompress any that were compressed
|
|
bool
|
|
doExtract(std::string* ErrMsg) {
|
|
if (buildPaths(false, ErrMsg))
|
|
return true;
|
|
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())) {
|
|
|
|
// 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);
|
|
|
|
// Get the data and its length
|
|
const char* data = reinterpret_cast<const char*>(I->getData());
|
|
unsigned len = I->getSize();
|
|
|
|
// Write the data, making sure to uncompress things first
|
|
if (I->isCompressed()) {
|
|
Compressor::decompressToStream(data,len,file);
|
|
} else {
|
|
file.write(data,len);
|
|
}
|
|
file.close();
|
|
|
|
// If we're supposed to retain the original modification times, etc. do so
|
|
// now.
|
|
if (OriginalDates)
|
|
I->getPath().setStatusInfoOnDisk(I->getFileStatus());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// doDelete - Implement the delete operation. This function deletes zero or more
|
|
// members from the archive. Note that if the count is specified, there should
|
|
// be no more than one path in the Paths list or else this algorithm breaks.
|
|
// That check is enforced in parseCommandLine (above).
|
|
bool
|
|
doDelete(std::string* ErrMsg) {
|
|
if (buildPaths(false, 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--;
|
|
} else {
|
|
++I;
|
|
}
|
|
}
|
|
|
|
// We're done editting, reconstruct the archive.
|
|
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
|
|
return true;
|
|
if (ReallyVerbose)
|
|
printSymbolTable();
|
|
return false;
|
|
}
|
|
|
|
// doMore - Implement the move operation. This function re-arranges just the
|
|
// order of the archive members so that when the archive is written the move
|
|
// of the members is accomplished. Note the use of the RelPos variable to
|
|
// determine where the items should be moved to.
|
|
bool
|
|
doMove(std::string* ErrMsg) {
|
|
if (buildPaths(false, ErrMsg))
|
|
return true;
|
|
|
|
// By default and convention the place to move members to is the end of the
|
|
// archive.
|
|
Archive::iterator moveto_spot = TheArchive->end();
|
|
|
|
// 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) {
|
|
for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end();
|
|
I != E; ++I ) {
|
|
if (RelPos == I->getPath().toString()) {
|
|
if (AddAfter) {
|
|
moveto_spot = I;
|
|
moveto_spot++;
|
|
} else {
|
|
moveto_spot = I;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Keep a list of the paths remaining to be moved
|
|
std::set<sys::Path> 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());
|
|
if (found != remaining.end()) {
|
|
if (I != moveto_spot)
|
|
TheArchive->splice(moveto_spot,*TheArchive,I);
|
|
remaining.erase(found);
|
|
}
|
|
}
|
|
|
|
// We're done editting, reconstruct the archive.
|
|
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
|
|
return true;
|
|
if (ReallyVerbose)
|
|
printSymbolTable();
|
|
return false;
|
|
}
|
|
|
|
// doQuickAppend - Implements the 'q' operation. This function just
|
|
// indiscriminantly adds the members to the archive and rebuilds it.
|
|
bool
|
|
doQuickAppend(std::string* ErrMsg) {
|
|
// Get the list of paths to append.
|
|
if (buildPaths(true, ErrMsg))
|
|
return true;
|
|
if (Paths.empty())
|
|
return false;
|
|
|
|
// Append them quickly.
|
|
for (std::set<sys::Path>::iterator PI = Paths.begin(), PE = Paths.end();
|
|
PI != PE; ++PI) {
|
|
if (TheArchive->addFileBefore(*PI,TheArchive->end(),ErrMsg))
|
|
return true;
|
|
}
|
|
|
|
// We're done editting, reconstruct the archive.
|
|
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
|
|
return true;
|
|
if (ReallyVerbose)
|
|
printSymbolTable();
|
|
return false;
|
|
}
|
|
|
|
// doReplaceOrInsert - Implements the 'r' operation. This function will replace
|
|
// any existing files or insert new ones into the archive.
|
|
bool
|
|
doReplaceOrInsert(std::string* ErrMsg) {
|
|
|
|
// Build the list of files to be added/replaced.
|
|
if (buildPaths(true, ErrMsg))
|
|
return true;
|
|
if (Paths.empty())
|
|
return false;
|
|
|
|
// Keep track of the paths that remain to be inserted.
|
|
std::set<sys::Path> remaining(Paths);
|
|
|
|
// Default the insertion spot to the end of the archive
|
|
Archive::iterator insert_spot = TheArchive->end();
|
|
|
|
// Iterate over the archive contents
|
|
for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
|
|
I != E && !remaining.empty(); ++I ) {
|
|
|
|
// 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(),
|
|
RE = remaining.end(); RI != RE; ++RI ) {
|
|
std::string compare(RI->toString());
|
|
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);
|
|
}
|
|
if (compare == I->getPath().toString()) {
|
|
found = RI;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found != remaining.end()) {
|
|
sys::FileStatus si;
|
|
std::string Err;
|
|
if (found->getFileStatus(si, &Err))
|
|
return true;
|
|
if (si.isDir) {
|
|
if (OnlyUpdate) {
|
|
// Replace the item only if it is newer.
|
|
if (si.modTime > I->getModTime())
|
|
if (I->replaceWith(*found, ErrMsg))
|
|
return true;
|
|
} else {
|
|
// Replace the item regardless of time stamp
|
|
if (I->replaceWith(*found, ErrMsg))
|
|
return true;
|
|
}
|
|
} else {
|
|
// We purposefully ignore directories.
|
|
}
|
|
|
|
// Remove it from our "to do" list
|
|
remaining.erase(found);
|
|
}
|
|
|
|
// Determine if this is the place where we should insert
|
|
if ((AddBefore || InsertBefore) && (RelPos == I->getPath().toString()))
|
|
insert_spot = I;
|
|
else if (AddAfter && (RelPos == I->getPath().toString())) {
|
|
insert_spot = I;
|
|
insert_spot++;
|
|
}
|
|
}
|
|
|
|
// 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(),
|
|
PE = remaining.end(); PI != PE; ++PI) {
|
|
if (TheArchive->addFileBefore(*PI,insert_spot, ErrMsg))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// We're done editting, reconstruct the archive.
|
|
if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg))
|
|
return true;
|
|
if (ReallyVerbose)
|
|
printSymbolTable();
|
|
return false;
|
|
}
|
|
|
|
// main - main program for llvm-ar .. see comments in the code
|
|
int main(int argc, char **argv) {
|
|
|
|
// Have the command line options parsed and handle things
|
|
// like --help and --version.
|
|
cl::ParseCommandLineOptions(argc, argv,
|
|
" LLVM Archiver (llvm-ar)\n\n"
|
|
" This program archives bytecode files into single libraries\n"
|
|
);
|
|
|
|
// Print a stack trace if we signal out.
|
|
sys::PrintStackTraceOnErrorSignal();
|
|
|
|
int exitCode = 0;
|
|
|
|
// Make sure we don't exit with "unhandled exception".
|
|
try {
|
|
// Do our own parsing of the command line because the CommandLine utility
|
|
// 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))
|
|
throw std::string("Archive name invalid: ") + ArchiveName;
|
|
|
|
// Create or open the archive object.
|
|
if (!ArchivePath.exists()) {
|
|
// Produce a warning if we should and we're creating the archive
|
|
if (!Create)
|
|
std::cerr << argv[0] << ": creating " << ArchivePath.toString() << "\n";
|
|
TheArchive = Archive::CreateEmpty(ArchivePath);
|
|
} else {
|
|
std::string Error;
|
|
TheArchive = Archive::OpenAndLoad(ArchivePath, &Error);
|
|
if (TheArchive == 0) {
|
|
std::cerr << argv[0] << ": error loading '" << ArchivePath << "': "
|
|
<< Error << "!\n";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Make sure we're not fooling ourselves.
|
|
assert(TheArchive && "Unable to instantiate the archive");
|
|
|
|
// Make sure we clean up the archive even on failure.
|
|
std::auto_ptr<Archive> AutoArchive(TheArchive);
|
|
|
|
// Perform the operation
|
|
std::string ErrMsg;
|
|
bool haveError = false;
|
|
switch (Operation) {
|
|
case Print: haveError = doPrint(&ErrMsg); break;
|
|
case Delete: haveError = doDelete(&ErrMsg); break;
|
|
case Move: haveError = doMove(&ErrMsg); break;
|
|
case QuickAppend: haveError = doQuickAppend(&ErrMsg); break;
|
|
case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break;
|
|
case DisplayTable: haveError = doDisplayTable(&ErrMsg); break;
|
|
case Extract: haveError = doExtract(&ErrMsg); break;
|
|
case NoOperation:
|
|
std::cerr << argv[0] << ": No operation was selected.\n";
|
|
break;
|
|
}
|
|
if (haveError) {
|
|
std::cerr << argv[0] << ": " << ErrMsg << "\n";
|
|
return 1;
|
|
}
|
|
} catch (const char*msg) {
|
|
// These errors are usage errors, thrown only by the various checks in the
|
|
// code above.
|
|
std::cerr << argv[0] << ": " << msg << "\n\n";
|
|
cl::PrintHelpMessage();
|
|
exitCode = 1;
|
|
} catch (const std::string& msg) {
|
|
// These errors are thrown by LLVM libraries (e.g. lib System) and represent
|
|
// a more serious error so we bump the exitCode and don't print the usage.
|
|
std::cerr << argv[0] << ": " << msg << "\n";
|
|
exitCode = 2;
|
|
} catch (...) {
|
|
// This really shouldn't happen, but just in case ....
|
|
std::cerr << argv[0] << ": An unexpected unknown exception occurred.\n";
|
|
exitCode = 3;
|
|
}
|
|
|
|
// Return result code back to operating system.
|
|
return exitCode;
|
|
}
|