llvm-ar.cpp revision 35637fc6233c3ee275d497286241795986b6e432
1//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// Builds up (relatively) standard unix archive files (.a) containing LLVM 11// bitcode or other files. 12// 13//===----------------------------------------------------------------------===// 14 15#include "Archive.h" 16#include "llvm/IR/LLVMContext.h" 17#include "llvm/IR/Module.h" 18#include "llvm/Support/CommandLine.h" 19#include "llvm/Support/FileSystem.h" 20#include "llvm/Support/Format.h" 21#include "llvm/Support/ManagedStatic.h" 22#include "llvm/Support/PrettyStackTrace.h" 23#include "llvm/Support/Signals.h" 24#include "llvm/Support/raw_ostream.h" 25#include <algorithm> 26#include <cstdlib> 27#include <fcntl.h> 28#include <memory> 29 30#if !defined(_MSC_VER) && !defined(__MINGW32__) 31#include <unistd.h> 32#else 33#include <io.h> 34#endif 35 36using namespace llvm; 37 38// Option for compatibility with AIX, not used but must allow it to be present. 39static cl::opt<bool> 40X32Option ("X32_64", cl::Hidden, 41 cl::desc("Ignored option for compatibility with AIX")); 42 43// llvm-ar operation code and modifier flags. This must come first. 44static cl::opt<std::string> 45Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); 46 47// llvm-ar remaining positional arguments. 48static cl::list<std::string> 49RestOfArgs(cl::Positional, cl::OneOrMore, 50 cl::desc("[relpos] [count] <archive-file> [members]...")); 51 52// MoreHelp - Provide additional help output explaining the operations and 53// modifiers of llvm-ar. This object instructs the CommandLine library 54// to print the text of the constructor when the --help option is given. 55static cl::extrahelp MoreHelp( 56 "\nOPERATIONS:\n" 57 " d[NsS] - delete file(s) from the archive\n" 58 " m[abiSs] - move file(s) in the archive\n" 59 " p[kN] - print file(s) found in the archive\n" 60 " q[ufsS] - quick append file(s) to the archive\n" 61 " r[abfiuRsS] - replace or insert file(s) into the archive\n" 62 " t - display contents of archive\n" 63 " x[No] - extract file(s) from the archive\n" 64 "\nMODIFIERS (operation specific):\n" 65 " [a] - put file(s) after [relpos]\n" 66 " [b] - put file(s) before [relpos] (same as [i])\n" 67 " [f] - truncate inserted file names\n" 68 " [i] - put file(s) before [relpos] (same as [b])\n" 69 " [k] - always print bitcode files (default is to skip them)\n" 70 " [N] - use instance [count] of name\n" 71 " [o] - preserve original dates\n" 72 " [P] - use full path names when matching\n" 73 " [R] - recurse through directories when inserting\n" 74 " [s] - create an archive index (cf. ranlib)\n" 75 " [S] - do not build a symbol table\n" 76 " [u] - update only files newer than archive contents\n" 77 "\nMODIFIERS (generic):\n" 78 " [c] - do not warn if the library had to be created\n" 79 " [v] - be verbose about actions taken\n" 80 " [V] - be *really* verbose about actions taken\n" 81); 82 83// This enumeration delineates the kinds of operations on an archive 84// that are permitted. 85enum ArchiveOperation { 86 Print, ///< Print the contents of the archive 87 Delete, ///< Delete the specified members 88 Move, ///< Move members to end or as given by {a,b,i} modifiers 89 QuickAppend, ///< Quickly append to end of archive 90 ReplaceOrInsert, ///< Replace or Insert members 91 DisplayTable, ///< Display the table of contents 92 Extract ///< Extract files back to file system 93}; 94 95// Modifiers to follow operation to vary behavior 96bool AddAfter = false; ///< 'a' modifier 97bool AddBefore = false; ///< 'b' modifier 98bool Create = false; ///< 'c' modifier 99bool TruncateNames = false; ///< 'f' modifier 100bool InsertBefore = false; ///< 'i' modifier 101bool UseCount = false; ///< 'N' modifier 102bool OriginalDates = false; ///< 'o' modifier 103bool FullPath = false; ///< 'P' modifier 104bool SymTable = true; ///< 's' & 'S' modifiers 105bool OnlyUpdate = false; ///< 'u' modifier 106bool Verbose = false; ///< 'v' modifier 107 108// Relative Positional Argument (for insert/move). This variable holds 109// the name of the archive member to which the 'a', 'b' or 'i' modifier 110// refers. Only one of 'a', 'b' or 'i' can be specified so we only need 111// one variable. 112std::string RelPos; 113 114// Select which of multiple entries in the archive with the same name should be 115// used (specified with -N) for the delete and extract operations. 116int Count = 1; 117 118// This variable holds the name of the archive file as given on the 119// command line. 120std::string ArchiveName; 121 122// This variable holds the list of member files to proecess, as given 123// on the command line. 124std::vector<std::string> Members; 125 126// This variable holds the (possibly expanded) list of path objects that 127// correspond to files we will 128std::set<std::string> Paths; 129 130// The Archive object to which all the editing operations will be sent. 131Archive* TheArchive = 0; 132 133// The name this program was invoked as. 134static const char *program_name; 135 136// show_help - Show the error message, the help message and exit. 137LLVM_ATTRIBUTE_NORETURN static void 138show_help(const std::string &msg) { 139 errs() << program_name << ": " << msg << "\n\n"; 140 cl::PrintHelpMessage(); 141 if (TheArchive) 142 delete TheArchive; 143 std::exit(1); 144} 145 146// fail - Show the error message and exit. 147LLVM_ATTRIBUTE_NORETURN static void 148fail(const std::string &msg) { 149 errs() << program_name << ": " << msg << "\n\n"; 150 if (TheArchive) 151 delete TheArchive; 152 std::exit(1); 153} 154 155// getRelPos - Extract the member filename from the command line for 156// the [relpos] argument associated with a, b, and i modifiers 157void getRelPos() { 158 if(RestOfArgs.size() == 0) 159 show_help("Expected [relpos] for a, b, or i modifier"); 160 RelPos = RestOfArgs[0]; 161 RestOfArgs.erase(RestOfArgs.begin()); 162} 163 164// getCount - Extract the [count] argument associated with the N modifier 165// from the command line and check its value. 166void getCount() { 167 if(RestOfArgs.size() == 0) 168 show_help("Expected [count] value with N modifier"); 169 170 Count = atoi(RestOfArgs[0].c_str()); 171 RestOfArgs.erase(RestOfArgs.begin()); 172 173 // Non-positive counts are not allowed 174 if (Count < 1) 175 show_help("Invalid [count] value (not a positive integer)"); 176} 177 178// getArchive - Get the archive file name from the command line 179void getArchive() { 180 if(RestOfArgs.size() == 0) 181 show_help("An archive name must be specified"); 182 ArchiveName = RestOfArgs[0]; 183 RestOfArgs.erase(RestOfArgs.begin()); 184} 185 186// getMembers - Copy over remaining items in RestOfArgs to our Members vector 187// This is just for clarity. 188void getMembers() { 189 if(RestOfArgs.size() > 0) 190 Members = std::vector<std::string>(RestOfArgs); 191} 192 193// parseCommandLine - Parse the command line options as presented and return the 194// operation specified. Process all modifiers and check to make sure that 195// constraints on modifier/operation pairs have not been violated. 196ArchiveOperation parseCommandLine() { 197 198 // Keep track of number of operations. We can only specify one 199 // per execution. 200 unsigned NumOperations = 0; 201 202 // Keep track of the number of positional modifiers (a,b,i). Only 203 // one can be specified. 204 unsigned NumPositional = 0; 205 206 // Keep track of which operation was requested 207 ArchiveOperation Operation; 208 209 for(unsigned i=0; i<Options.size(); ++i) { 210 switch(Options[i]) { 211 case 'd': ++NumOperations; Operation = Delete; break; 212 case 'm': ++NumOperations; Operation = Move ; break; 213 case 'p': ++NumOperations; Operation = Print; break; 214 case 'q': ++NumOperations; Operation = QuickAppend; break; 215 case 'r': ++NumOperations; Operation = ReplaceOrInsert; break; 216 case 't': ++NumOperations; Operation = DisplayTable; break; 217 case 'x': ++NumOperations; Operation = Extract; break; 218 case 'c': Create = true; break; 219 case 'f': TruncateNames = true; break; 220 case 'l': /* accepted but unused */ break; 221 case 'o': OriginalDates = true; break; 222 case 's': break; // Ignore for now. 223 case 'S': break; // Ignore for now. 224 case 'P': FullPath = true; break; 225 case 'u': OnlyUpdate = true; break; 226 case 'v': Verbose = true; break; 227 case 'a': 228 getRelPos(); 229 AddAfter = true; 230 NumPositional++; 231 break; 232 case 'b': 233 getRelPos(); 234 AddBefore = true; 235 NumPositional++; 236 break; 237 case 'i': 238 getRelPos(); 239 InsertBefore = true; 240 NumPositional++; 241 break; 242 case 'N': 243 getCount(); 244 UseCount = true; 245 break; 246 default: 247 cl::PrintHelpMessage(); 248 } 249 } 250 251 // At this point, the next thing on the command line must be 252 // the archive name. 253 getArchive(); 254 255 // Everything on the command line at this point is a member. 256 getMembers(); 257 258 // Perform various checks on the operation/modifier specification 259 // to make sure we are dealing with a legal request. 260 if (NumOperations == 0) 261 show_help("You must specify at least one of the operations"); 262 if (NumOperations > 1) 263 show_help("Only one operation may be specified"); 264 if (NumPositional > 1) 265 show_help("You may only specify one of a, b, and i modifiers"); 266 if (AddAfter || AddBefore || InsertBefore) { 267 if (Operation != Move && Operation != ReplaceOrInsert) 268 show_help("The 'a', 'b' and 'i' modifiers can only be specified with " 269 "the 'm' or 'r' operations"); 270 } 271 if (OriginalDates && Operation != Extract) 272 show_help("The 'o' modifier is only applicable to the 'x' operation"); 273 if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert) 274 show_help("The 'f' modifier is only applicable to the 'q' and 'r' " 275 "operations"); 276 if (OnlyUpdate && Operation != ReplaceOrInsert) 277 show_help("The 'u' modifier is only applicable to the 'r' operation"); 278 if (Count > 1 && Members.size() > 1) 279 show_help("Only one member name may be specified with the 'N' modifier"); 280 281 // Return the parsed operation to the caller 282 return Operation; 283} 284 285// buildPaths - Convert the strings in the Members vector to sys::Path objects 286// and make sure they are valid and exist exist. This check is only needed for 287// the operations that add/replace files to the archive ('q' and 'r') 288bool buildPaths(bool checkExistence, std::string* ErrMsg) { 289 for (unsigned i = 0; i < Members.size(); i++) { 290 std::string aPath = Members[i]; 291 if (checkExistence) { 292 bool IsDirectory; 293 error_code EC = sys::fs::is_directory(aPath, IsDirectory); 294 if (EC) 295 fail(aPath + ": " + EC.message()); 296 if (IsDirectory) 297 fail(aPath + " Is a directory"); 298 299 Paths.insert(aPath); 300 } else { 301 Paths.insert(aPath); 302 } 303 } 304 return false; 305} 306 307// doPrint - Implements the 'p' operation. This function traverses the archive 308// looking for members that match the path list. It is careful to uncompress 309// things that should be and to skip bitcode files unless the 'k' modifier was 310// given. 311bool doPrint(std::string* ErrMsg) { 312 if (buildPaths(false, ErrMsg)) 313 return true; 314 unsigned countDown = Count; 315 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 316 I != E; ++I ) { 317 if (Paths.empty() || 318 (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { 319 if (countDown == 1) { 320 const char* data = reinterpret_cast<const char*>(I->getData()); 321 322 // Skip things that don't make sense to print 323 if (I->isSVR4SymbolTable() || I->isBSD4SymbolTable()) 324 continue; 325 326 if (Verbose) 327 outs() << "Printing " << I->getPath().str() << "\n"; 328 329 unsigned len = I->getSize(); 330 outs().write(data, len); 331 } else { 332 countDown--; 333 } 334 } 335 } 336 return false; 337} 338 339// putMode - utility function for printing out the file mode when the 't' 340// operation is in verbose mode. 341void 342printMode(unsigned mode) { 343 if (mode & 004) 344 outs() << "r"; 345 else 346 outs() << "-"; 347 if (mode & 002) 348 outs() << "w"; 349 else 350 outs() << "-"; 351 if (mode & 001) 352 outs() << "x"; 353 else 354 outs() << "-"; 355} 356 357// doDisplayTable - Implement the 't' operation. This function prints out just 358// the file names of each of the members. However, if verbose mode is requested 359// ('v' modifier) then the file type, permission mode, user, group, size, and 360// modification time are also printed. 361bool 362doDisplayTable(std::string* ErrMsg) { 363 if (buildPaths(false, ErrMsg)) 364 return true; 365 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 366 I != E; ++I ) { 367 if (Paths.empty() || 368 (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { 369 if (Verbose) { 370 // FIXME: Output should be this format: 371 // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile 372 outs() << " "; 373 unsigned mode = I->getMode(); 374 printMode((mode >> 6) & 007); 375 printMode((mode >> 3) & 007); 376 printMode(mode & 007); 377 outs() << " " << format("%4u", I->getUser()); 378 outs() << "/" << format("%4u", I->getGroup()); 379 outs() << " " << format("%8u", I->getSize()); 380 outs() << " " << format("%20s", I->getModTime().str().substr(4).c_str()); 381 outs() << " " << I->getPath().str() << "\n"; 382 } else { 383 outs() << I->getPath().str() << "\n"; 384 } 385 } 386 } 387 return false; 388} 389 390// doExtract - Implement the 'x' operation. This function extracts files back to 391// the file system. 392bool 393doExtract(std::string* ErrMsg) { 394 if (buildPaths(false, ErrMsg)) 395 return true; 396 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 397 I != E; ++I ) { 398 if (Paths.empty() || 399 (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { 400 401 // Open up a file stream for writing 402 int OpenFlags = O_TRUNC | O_WRONLY | O_CREAT; 403#ifdef O_BINARY 404 OpenFlags |= O_BINARY; 405#endif 406 407 // Retain the original mode. 408 sys::fs::perms Mode = sys::fs::perms(I->getMode()); 409 410 int FD = open(I->getPath().str().c_str(), OpenFlags, Mode); 411 if (FD < 0) 412 return true; 413 414 { 415 raw_fd_ostream file(FD, false); 416 417 // Get the data and its length 418 const char* data = reinterpret_cast<const char*>(I->getData()); 419 unsigned len = I->getSize(); 420 421 // Write the data. 422 file.write(data, len); 423 } 424 425 // If we're supposed to retain the original modification times, etc. do so 426 // now. 427 if (OriginalDates) { 428 error_code EC = 429 sys::fs::setLastModificationAndAccessTime(FD, I->getModTime()); 430 if (EC) 431 fail(EC.message()); 432 } 433 if (close(FD)) 434 return true; 435 } 436 } 437 return false; 438} 439 440// doDelete - Implement the delete operation. This function deletes zero or more 441// members from the archive. Note that if the count is specified, there should 442// be no more than one path in the Paths list or else this algorithm breaks. 443// That check is enforced in parseCommandLine (above). 444bool 445doDelete(std::string* ErrMsg) { 446 if (buildPaths(false, ErrMsg)) 447 return true; 448 if (Paths.empty()) 449 return false; 450 unsigned countDown = Count; 451 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 452 I != E; ) { 453 if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) { 454 if (countDown == 1) { 455 Archive::iterator J = I; 456 ++I; 457 TheArchive->erase(J); 458 } else 459 countDown--; 460 } else { 461 ++I; 462 } 463 } 464 465 // We're done editting, reconstruct the archive. 466 if (TheArchive->writeToDisk(TruncateNames,ErrMsg)) 467 return true; 468 return false; 469} 470 471// doMore - Implement the move operation. This function re-arranges just the 472// order of the archive members so that when the archive is written the move 473// of the members is accomplished. Note the use of the RelPos variable to 474// determine where the items should be moved to. 475bool 476doMove(std::string* ErrMsg) { 477 if (buildPaths(false, ErrMsg)) 478 return true; 479 480 // By default and convention the place to move members to is the end of the 481 // archive. 482 Archive::iterator moveto_spot = TheArchive->end(); 483 484 // However, if the relative positioning modifiers were used, we need to scan 485 // the archive to find the member in question. If we don't find it, its no 486 // crime, we just move to the end. 487 if (AddBefore || InsertBefore || AddAfter) { 488 for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); 489 I != E; ++I ) { 490 if (RelPos == I->getPath().str()) { 491 if (AddAfter) { 492 moveto_spot = I; 493 moveto_spot++; 494 } else { 495 moveto_spot = I; 496 } 497 break; 498 } 499 } 500 } 501 502 // Keep a list of the paths remaining to be moved 503 std::set<std::string> remaining(Paths); 504 505 // Scan the archive again, this time looking for the members to move to the 506 // moveto_spot. 507 for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); 508 I != E && !remaining.empty(); ++I ) { 509 std::set<std::string>::iterator found = 510 std::find(remaining.begin(),remaining.end(), I->getPath()); 511 if (found != remaining.end()) { 512 if (I != moveto_spot) 513 TheArchive->splice(moveto_spot,*TheArchive,I); 514 remaining.erase(found); 515 } 516 } 517 518 // We're done editting, reconstruct the archive. 519 if (TheArchive->writeToDisk(TruncateNames,ErrMsg)) 520 return true; 521 return false; 522} 523 524// doQuickAppend - Implements the 'q' operation. This function just 525// indiscriminantly adds the members to the archive and rebuilds it. 526bool 527doQuickAppend(std::string* ErrMsg) { 528 // Get the list of paths to append. 529 if (buildPaths(true, ErrMsg)) 530 return true; 531 if (Paths.empty()) 532 return false; 533 534 // Append them quickly. 535 for (std::set<std::string>::iterator PI = Paths.begin(), PE = Paths.end(); 536 PI != PE; ++PI) { 537 if (TheArchive->addFileBefore(*PI, TheArchive->end(), ErrMsg)) 538 return true; 539 } 540 541 // We're done editting, reconstruct the archive. 542 if (TheArchive->writeToDisk(TruncateNames,ErrMsg)) 543 return true; 544 return false; 545} 546 547// doReplaceOrInsert - Implements the 'r' operation. This function will replace 548// any existing files or insert new ones into the archive. 549bool 550doReplaceOrInsert(std::string* ErrMsg) { 551 552 // Build the list of files to be added/replaced. 553 if (buildPaths(true, ErrMsg)) 554 return true; 555 if (Paths.empty()) 556 return false; 557 558 // Keep track of the paths that remain to be inserted. 559 std::set<std::string> remaining(Paths); 560 561 // Default the insertion spot to the end of the archive 562 Archive::iterator insert_spot = TheArchive->end(); 563 564 // Iterate over the archive contents 565 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 566 I != E && !remaining.empty(); ++I ) { 567 568 // Determine if this archive member matches one of the paths we're trying 569 // to replace. 570 571 std::set<std::string>::iterator found = remaining.end(); 572 for (std::set<std::string>::iterator RI = remaining.begin(), 573 RE = remaining.end(); RI != RE; ++RI ) { 574 std::string compare(sys::path::filename(*RI)); 575 if (TruncateNames && compare.length() > 15) { 576 const char* nm = compare.c_str(); 577 unsigned len = compare.length(); 578 size_t slashpos = compare.rfind('/'); 579 if (slashpos != std::string::npos) { 580 nm += slashpos + 1; 581 len -= slashpos +1; 582 } 583 if (len > 15) 584 len = 15; 585 compare.assign(nm,len); 586 } 587 if (compare == I->getPath().str()) { 588 found = RI; 589 break; 590 } 591 } 592 593 if (found != remaining.end()) { 594 sys::fs::file_status Status; 595 error_code EC = sys::fs::status(*found, Status); 596 if (EC) 597 return true; 598 if (!sys::fs::is_directory(Status)) { 599 if (OnlyUpdate) { 600 // Replace the item only if it is newer. 601 if (Status.getLastModificationTime() > I->getModTime()) 602 if (I->replaceWith(*found, ErrMsg)) 603 return true; 604 } else { 605 // Replace the item regardless of time stamp 606 if (I->replaceWith(*found, ErrMsg)) 607 return true; 608 } 609 } else { 610 // We purposefully ignore directories. 611 } 612 613 // Remove it from our "to do" list 614 remaining.erase(found); 615 } 616 617 // Determine if this is the place where we should insert 618 if ((AddBefore || InsertBefore) && RelPos == I->getPath().str()) 619 insert_spot = I; 620 else if (AddAfter && RelPos == I->getPath().str()) { 621 insert_spot = I; 622 insert_spot++; 623 } 624 } 625 626 // If we didn't replace all the members, some will remain and need to be 627 // inserted at the previously computed insert-spot. 628 if (!remaining.empty()) { 629 for (std::set<std::string>::iterator PI = remaining.begin(), 630 PE = remaining.end(); PI != PE; ++PI) { 631 if (TheArchive->addFileBefore(*PI, insert_spot, ErrMsg)) 632 return true; 633 } 634 } 635 636 // We're done editting, reconstruct the archive. 637 if (TheArchive->writeToDisk(TruncateNames,ErrMsg)) 638 return true; 639 return false; 640} 641 642bool shouldCreateArchive(ArchiveOperation Op) { 643 switch (Op) { 644 case Print: 645 case Delete: 646 case Move: 647 case DisplayTable: 648 case Extract: 649 return false; 650 651 case QuickAppend: 652 case ReplaceOrInsert: 653 return true; 654 } 655 656 llvm_unreachable("Missing entry in covered switch."); 657} 658 659// main - main program for llvm-ar .. see comments in the code 660int main(int argc, char **argv) { 661 program_name = argv[0]; 662 // Print a stack trace if we signal out. 663 sys::PrintStackTraceOnErrorSignal(); 664 PrettyStackTraceProgram X(argc, argv); 665 LLVMContext &Context = getGlobalContext(); 666 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 667 668 // Have the command line options parsed and handle things 669 // like --help and --version. 670 cl::ParseCommandLineOptions(argc, argv, 671 "LLVM Archiver (llvm-ar)\n\n" 672 " This program archives bitcode files into single libraries\n" 673 ); 674 675 int exitCode = 0; 676 677 // Do our own parsing of the command line because the CommandLine utility 678 // can't handle the grouped positional parameters without a dash. 679 ArchiveOperation Operation = parseCommandLine(); 680 681 // Create or open the archive object. 682 if (shouldCreateArchive(Operation) && !llvm::sys::fs::exists(ArchiveName)) { 683 // Produce a warning if we should and we're creating the archive 684 if (!Create) 685 errs() << argv[0] << ": creating " << ArchiveName << "\n"; 686 TheArchive = Archive::CreateEmpty(ArchiveName, Context); 687 TheArchive->writeToDisk(); 688 } 689 690 if (!TheArchive) { 691 std::string Error; 692 TheArchive = Archive::OpenAndLoad(ArchiveName, Context, &Error); 693 if (TheArchive == 0) { 694 errs() << argv[0] << ": error loading '" << ArchiveName << "': " 695 << Error << "!\n"; 696 return 1; 697 } 698 } 699 700 // Make sure we're not fooling ourselves. 701 assert(TheArchive && "Unable to instantiate the archive"); 702 703 // Perform the operation 704 std::string ErrMsg; 705 bool haveError = false; 706 switch (Operation) { 707 case Print: haveError = doPrint(&ErrMsg); break; 708 case Delete: haveError = doDelete(&ErrMsg); break; 709 case Move: haveError = doMove(&ErrMsg); break; 710 case QuickAppend: haveError = doQuickAppend(&ErrMsg); break; 711 case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break; 712 case DisplayTable: haveError = doDisplayTable(&ErrMsg); break; 713 case Extract: haveError = doExtract(&ErrMsg); break; 714 } 715 if (haveError) { 716 errs() << argv[0] << ": " << ErrMsg << "\n"; 717 return 1; 718 } 719 720 delete TheArchive; 721 TheArchive = 0; 722 723 // Return result code back to operating system. 724 return exitCode; 725} 726