1 //===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Builds up (relatively) standard unix archive files (.a) containing LLVM
11 // bitcode or other files.
13 //===----------------------------------------------------------------------===//
15 #include "llvm/LLVMContext.h"
16 #include "llvm/Module.h"
17 #include "llvm/Bitcode/Archive.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/ManagedStatic.h"
20 #include "llvm/Support/PrettyStackTrace.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include "llvm/System/Signals.h"
30 // Option for compatibility with AIX, not used but must allow it to be present.
32 X32Option ("X32_64", cl::Hidden
,
33 cl::desc("Ignored option for compatibility with AIX"));
35 // llvm-ar operation code and modifier flags. This must come first.
36 static cl::opt
<std::string
>
37 Options(cl::Positional
, cl::Required
, cl::desc("{operation}[modifiers]..."));
39 // llvm-ar remaining positional arguments.
40 static cl::list
<std::string
>
41 RestOfArgs(cl::Positional
, cl::OneOrMore
,
42 cl::desc("[relpos] [count] <archive-file> [members]..."));
44 // MoreHelp - Provide additional help output explaining the operations and
45 // modifiers of llvm-ar. This object instructs the CommandLine library
46 // to print the text of the constructor when the --help option is given.
47 static cl::extrahelp
MoreHelp(
49 " d[NsS] - delete file(s) from the archive\n"
50 " m[abiSs] - move file(s) in the archive\n"
51 " p[kN] - print file(s) found in the archive\n"
52 " q[ufsS] - quick append file(s) to the archive\n"
53 " r[abfiuzRsS] - replace or insert file(s) into the archive\n"
54 " t - display contents of archive\n"
55 " x[No] - extract file(s) from the archive\n"
56 "\nMODIFIERS (operation specific):\n"
57 " [a] - put file(s) after [relpos]\n"
58 " [b] - put file(s) before [relpos] (same as [i])\n"
59 " [f] - truncate inserted file names\n"
60 " [i] - put file(s) before [relpos] (same as [b])\n"
61 " [k] - always print bitcode files (default is to skip them)\n"
62 " [N] - use instance [count] of name\n"
63 " [o] - preserve original dates\n"
64 " [P] - use full path names when matching\n"
65 " [R] - recurse through directories when inserting\n"
66 " [s] - create an archive index (cf. ranlib)\n"
67 " [S] - do not build a symbol table\n"
68 " [u] - update only files newer than archive contents\n"
69 " [z] - compress files before inserting/extracting\n"
70 "\nMODIFIERS (generic):\n"
71 " [c] - do not warn if the library had to be created\n"
72 " [v] - be verbose about actions taken\n"
73 " [V] - be *really* verbose about actions taken\n"
76 // This enumeration delineates the kinds of operations on an archive
77 // that are permitted.
78 enum ArchiveOperation
{
79 NoOperation
, ///< An operation hasn't been specified
80 Print
, ///< Print the contents of the archive
81 Delete
, ///< Delete the specified members
82 Move
, ///< Move members to end or as given by {a,b,i} modifiers
83 QuickAppend
, ///< Quickly append to end of archive
84 ReplaceOrInsert
, ///< Replace or Insert members
85 DisplayTable
, ///< Display the table of contents
86 Extract
///< Extract files back to file system
89 // Modifiers to follow operation to vary behavior
90 bool AddAfter
= false; ///< 'a' modifier
91 bool AddBefore
= false; ///< 'b' modifier
92 bool Create
= false; ///< 'c' modifier
93 bool TruncateNames
= false; ///< 'f' modifier
94 bool InsertBefore
= false; ///< 'i' modifier
95 bool DontSkipBitcode
= false; ///< 'k' modifier
96 bool UseCount
= false; ///< 'N' modifier
97 bool OriginalDates
= false; ///< 'o' modifier
98 bool FullPath
= false; ///< 'P' modifier
99 bool RecurseDirectories
= false; ///< 'R' modifier
100 bool SymTable
= true; ///< 's' & 'S' modifiers
101 bool OnlyUpdate
= false; ///< 'u' modifier
102 bool Verbose
= false; ///< 'v' modifier
103 bool ReallyVerbose
= false; ///< 'V' modifier
104 bool Compression
= false; ///< 'z' modifier
106 // Relative Positional Argument (for insert/move). This variable holds
107 // the name of the archive member to which the 'a', 'b' or 'i' modifier
108 // refers. Only one of 'a', 'b' or 'i' can be specified so we only need
112 // Select which of multiple entries in the archive with the same name should be
113 // used (specified with -N) for the delete and extract operations.
116 // This variable holds the name of the archive file as given on the
118 std::string ArchiveName
;
120 // This variable holds the list of member files to proecess, as given
121 // on the command line.
122 std::vector
<std::string
> Members
;
124 // This variable holds the (possibly expanded) list of path objects that
125 // correspond to files we will
126 std::set
<sys::Path
> Paths
;
128 // The Archive object to which all the editing operations will be sent.
129 Archive
* TheArchive
= 0;
131 // getRelPos - Extract the member filename from the command line for
132 // the [relpos] argument associated with a, b, and i modifiers
134 if(RestOfArgs
.size() > 0) {
135 RelPos
= RestOfArgs
[0];
136 RestOfArgs
.erase(RestOfArgs
.begin());
139 throw "Expected [relpos] for a, b, or i modifier";
142 // getCount - Extract the [count] argument associated with the N modifier
143 // from the command line and check its value.
145 if(RestOfArgs
.size() > 0) {
146 Count
= atoi(RestOfArgs
[0].c_str());
147 RestOfArgs
.erase(RestOfArgs
.begin());
150 throw "Expected [count] value with N modifier";
152 // Non-positive counts are not allowed
154 throw "Invalid [count] value (not a positive integer)";
157 // getArchive - Get the archive file name from the command line
159 if(RestOfArgs
.size() > 0) {
160 ArchiveName
= RestOfArgs
[0];
161 RestOfArgs
.erase(RestOfArgs
.begin());
164 throw "An archive name must be specified.";
167 // getMembers - Copy over remaining items in RestOfArgs to our Members vector
168 // This is just for clarity.
170 if(RestOfArgs
.size() > 0)
171 Members
= std::vector
<std::string
>(RestOfArgs
);
174 // parseCommandLine - Parse the command line options as presented and return the
175 // operation specified. Process all modifiers and check to make sure that
176 // constraints on modifier/operation pairs have not been violated.
177 ArchiveOperation
parseCommandLine() {
179 // Keep track of number of operations. We can only specify one
181 unsigned NumOperations
= 0;
183 // Keep track of the number of positional modifiers (a,b,i). Only
184 // one can be specified.
185 unsigned NumPositional
= 0;
187 // Keep track of which operation was requested
188 ArchiveOperation Operation
= NoOperation
;
190 for(unsigned i
=0; i
<Options
.size(); ++i
) {
192 case 'd': ++NumOperations
; Operation
= Delete
; break;
193 case 'm': ++NumOperations
; Operation
= Move
; break;
194 case 'p': ++NumOperations
; Operation
= Print
; break;
195 case 'q': ++NumOperations
; Operation
= QuickAppend
; break;
196 case 'r': ++NumOperations
; Operation
= ReplaceOrInsert
; break;
197 case 't': ++NumOperations
; Operation
= DisplayTable
; break;
198 case 'x': ++NumOperations
; Operation
= Extract
; break;
199 case 'c': Create
= true; break;
200 case 'f': TruncateNames
= true; break;
201 case 'k': DontSkipBitcode
= true; break;
202 case 'l': /* accepted but unused */ break;
203 case 'o': OriginalDates
= true; break;
204 case 'P': FullPath
= true; break;
205 case 'R': RecurseDirectories
= true; break;
206 case 's': SymTable
= true; break;
207 case 'S': SymTable
= false; break;
208 case 'u': OnlyUpdate
= true; break;
209 case 'v': Verbose
= true; break;
210 case 'V': Verbose
= ReallyVerbose
= true; break;
211 case 'z': Compression
= true; break;
232 cl::PrintHelpMessage();
236 // At this point, the next thing on the command line must be
240 // Everything on the command line at this point is a member.
243 // Perform various checks on the operation/modifier specification
244 // to make sure we are dealing with a legal request.
245 if (NumOperations
== 0)
246 throw "You must specify at least one of the operations";
247 if (NumOperations
> 1)
248 throw "Only one operation may be specified";
249 if (NumPositional
> 1)
250 throw "You may only specify one of a, b, and i modifiers";
251 if (AddAfter
|| AddBefore
|| InsertBefore
)
252 if (Operation
!= Move
&& Operation
!= ReplaceOrInsert
)
253 throw "The 'a', 'b' and 'i' modifiers can only be specified with "
254 "the 'm' or 'r' operations";
255 if (RecurseDirectories
&& Operation
!= ReplaceOrInsert
)
256 throw "The 'R' modifiers is only applicabe to the 'r' operation";
257 if (OriginalDates
&& Operation
!= Extract
)
258 throw "The 'o' modifier is only applicable to the 'x' operation";
259 if (TruncateNames
&& Operation
!=QuickAppend
&& Operation
!=ReplaceOrInsert
)
260 throw "The 'f' modifier is only applicable to the 'q' and 'r' operations";
261 if (OnlyUpdate
&& Operation
!= ReplaceOrInsert
)
262 throw "The 'u' modifier is only applicable to the 'r' operation";
263 if (Compression
&& Operation
!=ReplaceOrInsert
&& Operation
!=Extract
)
264 throw "The 'z' modifier is only applicable to the 'r' and 'x' operations";
265 if (Count
> 1 && Members
.size() > 1)
266 throw "Only one member name may be specified with the 'N' modifier";
268 // Return the parsed operation to the caller
272 // recurseDirectories - Implements the "R" modifier. This function scans through
273 // the Paths vector (built by buildPaths, below) and replaces any directories it
274 // finds with all the files in that directory (recursively). It uses the
275 // sys::Path::getDirectoryContent method to perform the actual directory scans.
277 recurseDirectories(const sys::Path
& path
,
278 std::set
<sys::Path
>& result
, std::string
* ErrMsg
) {
280 if (RecurseDirectories
) {
281 std::set
<sys::Path
> content
;
282 if (path
.getDirectoryContents(content
, ErrMsg
))
285 for (std::set
<sys::Path
>::iterator I
= content
.begin(), E
= content
.end();
287 // Make sure it exists and is a directory
288 sys::PathWithStatus
PwS(*I
);
289 const sys::FileStatus
*Status
= PwS
.getFileStatus(false, ErrMsg
);
293 std::set
<sys::Path
> moreResults
;
294 if (recurseDirectories(*I
, moreResults
, ErrMsg
))
296 result
.insert(moreResults
.begin(), moreResults
.end());
305 // buildPaths - Convert the strings in the Members vector to sys::Path objects
306 // and make sure they are valid and exist exist. This check is only needed for
307 // the operations that add/replace files to the archive ('q' and 'r')
308 bool buildPaths(bool checkExistence
, std::string
* ErrMsg
) {
309 for (unsigned i
= 0; i
< Members
.size(); i
++) {
311 if (!aPath
.set(Members
[i
]))
312 throw std::string("File member name invalid: ") + Members
[i
];
313 if (checkExistence
) {
315 throw std::string("File does not exist: ") + Members
[i
];
317 sys::PathWithStatus
PwS(aPath
);
318 const sys::FileStatus
*si
= PwS
.getFileStatus(false, &Err
);
322 std::set
<sys::Path
> dirpaths
;
323 if (recurseDirectories(aPath
, dirpaths
, ErrMsg
))
325 Paths
.insert(dirpaths
.begin(),dirpaths
.end());
336 // printSymbolTable - print out the archive's symbol table.
337 void printSymbolTable() {
338 std::cout
<< "\nArchive Symbol Table:\n";
339 const Archive::SymTabType
& symtab
= TheArchive
->getSymbolTable();
340 for (Archive::SymTabType::const_iterator I
=symtab
.begin(), E
=symtab
.end();
342 unsigned offset
= TheArchive
->getFirstFileOffset() + I
->second
;
343 std::cout
<< " " << std::setw(9) << offset
<< "\t" << I
->first
<<"\n";
347 // doPrint - Implements the 'p' operation. This function traverses the archive
348 // looking for members that match the path list. It is careful to uncompress
349 // things that should be and to skip bitcode files unless the 'k' modifier was
351 bool doPrint(std::string
* ErrMsg
) {
352 if (buildPaths(false, ErrMsg
))
354 unsigned countDown
= Count
;
355 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
358 (std::find(Paths
.begin(), Paths
.end(), I
->getPath()) != Paths
.end())) {
359 if (countDown
== 1) {
360 const char* data
= reinterpret_cast<const char*>(I
->getData());
362 // Skip things that don't make sense to print
363 if (I
->isLLVMSymbolTable() || I
->isSVR4SymbolTable() ||
364 I
->isBSD4SymbolTable() || (!DontSkipBitcode
&& I
->isBitcode()))
368 std::cout
<< "Printing " << I
->getPath().str() << "\n";
370 unsigned len
= I
->getSize();
371 std::cout
.write(data
, len
);
380 // putMode - utility function for printing out the file mode when the 't'
381 // operation is in verbose mode.
383 printMode(unsigned mode
) {
398 // doDisplayTable - Implement the 't' operation. This function prints out just
399 // the file names of each of the members. However, if verbose mode is requested
400 // ('v' modifier) then the file type, permission mode, user, group, size, and
401 // modification time are also printed.
403 doDisplayTable(std::string
* ErrMsg
) {
404 if (buildPaths(false, ErrMsg
))
406 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
409 (std::find(Paths
.begin(), Paths
.end(), I
->getPath()) != Paths
.end())) {
411 // FIXME: Output should be this format:
412 // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile
415 else if (I
->isCompressed())
419 unsigned mode
= I
->getMode();
420 printMode((mode
>> 6) & 007);
421 printMode((mode
>> 3) & 007);
422 printMode(mode
& 007);
423 std::cout
<< " " << std::setw(4) << I
->getUser();
424 std::cout
<< "/" << std::setw(4) << I
->getGroup();
425 std::cout
<< " " << std::setw(8) << I
->getSize();
426 std::cout
<< " " << std::setw(20) << I
->getModTime().str().substr(4);
427 std::cout
<< " " << I
->getPath().str() << "\n";
429 std::cout
<< I
->getPath().str() << "\n";
438 // doExtract - Implement the 'x' operation. This function extracts files back to
439 // the file system, making sure to uncompress any that were compressed
441 doExtract(std::string
* ErrMsg
) {
442 if (buildPaths(false, ErrMsg
))
444 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
447 (std::find(Paths
.begin(), Paths
.end(), I
->getPath()) != Paths
.end())) {
449 // Make sure the intervening directories are created
451 sys::Path
dirs(I
->getPath());
452 dirs
.eraseComponent();
453 if (dirs
.createDirectoryOnDisk(/*create_parents=*/true, ErrMsg
))
457 // Open up a file stream for writing
458 std::ios::openmode io_mode
= std::ios::out
| std::ios::trunc
|
460 std::ofstream
file(I
->getPath().c_str(), io_mode
);
462 // Get the data and its length
463 const char* data
= reinterpret_cast<const char*>(I
->getData());
464 unsigned len
= I
->getSize();
467 file
.write(data
,len
);
470 // If we're supposed to retain the original modification times, etc. do so
473 I
->getPath().setStatusInfoOnDisk(I
->getFileStatus());
479 // doDelete - Implement the delete operation. This function deletes zero or more
480 // members from the archive. Note that if the count is specified, there should
481 // be no more than one path in the Paths list or else this algorithm breaks.
482 // That check is enforced in parseCommandLine (above).
484 doDelete(std::string
* ErrMsg
) {
485 if (buildPaths(false, ErrMsg
))
489 unsigned countDown
= Count
;
490 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
492 if (std::find(Paths
.begin(), Paths
.end(), I
->getPath()) != Paths
.end()) {
493 if (countDown
== 1) {
494 Archive::iterator J
= I
;
496 TheArchive
->erase(J
);
504 // We're done editting, reconstruct the archive.
505 if (TheArchive
->writeToDisk(SymTable
,TruncateNames
,Compression
,ErrMsg
))
512 // doMore - Implement the move operation. This function re-arranges just the
513 // order of the archive members so that when the archive is written the move
514 // of the members is accomplished. Note the use of the RelPos variable to
515 // determine where the items should be moved to.
517 doMove(std::string
* ErrMsg
) {
518 if (buildPaths(false, ErrMsg
))
521 // By default and convention the place to move members to is the end of the
523 Archive::iterator moveto_spot
= TheArchive
->end();
525 // However, if the relative positioning modifiers were used, we need to scan
526 // the archive to find the member in question. If we don't find it, its no
527 // crime, we just move to the end.
528 if (AddBefore
|| InsertBefore
|| AddAfter
) {
529 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
531 if (RelPos
== I
->getPath().str()) {
543 // Keep a list of the paths remaining to be moved
544 std::set
<sys::Path
> remaining(Paths
);
546 // Scan the archive again, this time looking for the members to move to the
548 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
549 I
!= E
&& !remaining
.empty(); ++I
) {
550 std::set
<sys::Path
>::iterator found
=
551 std::find(remaining
.begin(),remaining
.end(),I
->getPath());
552 if (found
!= remaining
.end()) {
553 if (I
!= moveto_spot
)
554 TheArchive
->splice(moveto_spot
,*TheArchive
,I
);
555 remaining
.erase(found
);
559 // We're done editting, reconstruct the archive.
560 if (TheArchive
->writeToDisk(SymTable
,TruncateNames
,Compression
,ErrMsg
))
567 // doQuickAppend - Implements the 'q' operation. This function just
568 // indiscriminantly adds the members to the archive and rebuilds it.
570 doQuickAppend(std::string
* ErrMsg
) {
571 // Get the list of paths to append.
572 if (buildPaths(true, ErrMsg
))
577 // Append them quickly.
578 for (std::set
<sys::Path
>::iterator PI
= Paths
.begin(), PE
= Paths
.end();
580 if (TheArchive
->addFileBefore(*PI
,TheArchive
->end(),ErrMsg
))
584 // We're done editting, reconstruct the archive.
585 if (TheArchive
->writeToDisk(SymTable
,TruncateNames
,Compression
,ErrMsg
))
592 // doReplaceOrInsert - Implements the 'r' operation. This function will replace
593 // any existing files or insert new ones into the archive.
595 doReplaceOrInsert(std::string
* ErrMsg
) {
597 // Build the list of files to be added/replaced.
598 if (buildPaths(true, ErrMsg
))
603 // Keep track of the paths that remain to be inserted.
604 std::set
<sys::Path
> remaining(Paths
);
606 // Default the insertion spot to the end of the archive
607 Archive::iterator insert_spot
= TheArchive
->end();
609 // Iterate over the archive contents
610 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
611 I
!= E
&& !remaining
.empty(); ++I
) {
613 // Determine if this archive member matches one of the paths we're trying
616 std::set
<sys::Path
>::iterator found
= remaining
.end();
617 for (std::set
<sys::Path
>::iterator RI
= remaining
.begin(),
618 RE
= remaining
.end(); RI
!= RE
; ++RI
) {
619 std::string
compare(RI
->str());
620 if (TruncateNames
&& compare
.length() > 15) {
621 const char* nm
= compare
.c_str();
622 unsigned len
= compare
.length();
623 size_t slashpos
= compare
.rfind('/');
624 if (slashpos
!= std::string::npos
) {
630 compare
.assign(nm
,len
);
632 if (compare
== I
->getPath().str()) {
638 if (found
!= remaining
.end()) {
640 sys::PathWithStatus
PwS(*found
);
641 const sys::FileStatus
*si
= PwS
.getFileStatus(false, &Err
);
646 // Replace the item only if it is newer.
647 if (si
->modTime
> I
->getModTime())
648 if (I
->replaceWith(*found
, ErrMsg
))
651 // Replace the item regardless of time stamp
652 if (I
->replaceWith(*found
, ErrMsg
))
656 // We purposefully ignore directories.
659 // Remove it from our "to do" list
660 remaining
.erase(found
);
663 // Determine if this is the place where we should insert
664 if ((AddBefore
|| InsertBefore
) && RelPos
== I
->getPath().str())
666 else if (AddAfter
&& RelPos
== I
->getPath().str()) {
672 // If we didn't replace all the members, some will remain and need to be
673 // inserted at the previously computed insert-spot.
674 if (!remaining
.empty()) {
675 for (std::set
<sys::Path
>::iterator PI
= remaining
.begin(),
676 PE
= remaining
.end(); PI
!= PE
; ++PI
) {
677 if (TheArchive
->addFileBefore(*PI
,insert_spot
, ErrMsg
))
682 // We're done editting, reconstruct the archive.
683 if (TheArchive
->writeToDisk(SymTable
,TruncateNames
,Compression
,ErrMsg
))
690 // main - main program for llvm-ar .. see comments in the code
691 int main(int argc
, char **argv
) {
692 // Print a stack trace if we signal out.
693 sys::PrintStackTraceOnErrorSignal();
694 PrettyStackTraceProgram
X(argc
, argv
);
695 LLVMContext
&Context
= getGlobalContext();
696 llvm_shutdown_obj Y
; // Call llvm_shutdown() on exit.
698 // Have the command line options parsed and handle things
699 // like --help and --version.
700 cl::ParseCommandLineOptions(argc
, argv
,
701 "LLVM Archiver (llvm-ar)\n\n"
702 " This program archives bitcode files into single libraries\n"
707 // Make sure we don't exit with "unhandled exception".
709 // Do our own parsing of the command line because the CommandLine utility
710 // can't handle the grouped positional parameters without a dash.
711 ArchiveOperation Operation
= parseCommandLine();
713 // Check the path name of the archive
714 sys::Path ArchivePath
;
715 if (!ArchivePath
.set(ArchiveName
))
716 throw std::string("Archive name invalid: ") + ArchiveName
;
718 // Create or open the archive object.
719 if (!ArchivePath
.exists()) {
720 // Produce a warning if we should and we're creating the archive
722 errs() << argv
[0] << ": creating " << ArchivePath
.str() << "\n";
723 TheArchive
= Archive::CreateEmpty(ArchivePath
, Context
);
724 TheArchive
->writeToDisk();
727 TheArchive
= Archive::OpenAndLoad(ArchivePath
, Context
, &Error
);
728 if (TheArchive
== 0) {
729 errs() << argv
[0] << ": error loading '" << ArchivePath
.str() << "': "
735 // Make sure we're not fooling ourselves.
736 assert(TheArchive
&& "Unable to instantiate the archive");
738 // Make sure we clean up the archive even on failure.
739 std::auto_ptr
<Archive
> AutoArchive(TheArchive
);
741 // Perform the operation
743 bool haveError
= false;
745 case Print
: haveError
= doPrint(&ErrMsg
); break;
746 case Delete
: haveError
= doDelete(&ErrMsg
); break;
747 case Move
: haveError
= doMove(&ErrMsg
); break;
748 case QuickAppend
: haveError
= doQuickAppend(&ErrMsg
); break;
749 case ReplaceOrInsert
: haveError
= doReplaceOrInsert(&ErrMsg
); break;
750 case DisplayTable
: haveError
= doDisplayTable(&ErrMsg
); break;
751 case Extract
: haveError
= doExtract(&ErrMsg
); break;
753 errs() << argv
[0] << ": No operation was selected.\n";
757 errs() << argv
[0] << ": " << ErrMsg
<< "\n";
760 } catch (const char*msg
) {
761 // These errors are usage errors, thrown only by the various checks in the
763 errs() << argv
[0] << ": " << msg
<< "\n\n";
764 cl::PrintHelpMessage();
766 } catch (const std::string
& msg
) {
767 // These errors are thrown by LLVM libraries (e.g. lib System) and represent
768 // a more serious error so we bump the exitCode and don't print the usage.
769 errs() << argv
[0] << ": " << msg
<< "\n";
772 // This really shouldn't happen, but just in case ....
773 errs() << argv
[0] << ": An unexpected unknown exception occurred.\n";
777 // Return result code back to operating system.