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