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"
29 // Option for compatibility with AIX, not used but must allow it to be present.
31 X32Option ("X32_64", cl::Hidden
,
32 cl::desc("Ignored option for compatibility with AIX"));
34 // llvm-ar operation code and modifier flags. This must come first.
35 static cl::opt
<std::string
>
36 Options(cl::Positional
, cl::Required
, cl::desc("{operation}[modifiers]..."));
38 // llvm-ar remaining positional arguments.
39 static cl::list
<std::string
>
40 RestOfArgs(cl::Positional
, cl::OneOrMore
,
41 cl::desc("[relpos] [count] <archive-file> [members]..."));
43 // MoreHelp - Provide additional help output explaining the operations and
44 // modifiers of llvm-ar. This object instructs the CommandLine library
45 // to print the text of the constructor when the --help option is given.
46 static cl::extrahelp
MoreHelp(
48 " d[NsS] - delete file(s) from the archive\n"
49 " m[abiSs] - move file(s) in the archive\n"
50 " p[kN] - print file(s) found in the archive\n"
51 " q[ufsS] - quick append file(s) to the archive\n"
52 " r[abfiuzRsS] - replace or insert file(s) into the archive\n"
53 " t - display contents of archive\n"
54 " x[No] - extract file(s) from the archive\n"
55 "\nMODIFIERS (operation specific):\n"
56 " [a] - put file(s) after [relpos]\n"
57 " [b] - put file(s) before [relpos] (same as [i])\n"
58 " [f] - truncate inserted file names\n"
59 " [i] - put file(s) before [relpos] (same as [b])\n"
60 " [k] - always print bitcode files (default is to skip them)\n"
61 " [N] - use instance [count] of name\n"
62 " [o] - preserve original dates\n"
63 " [P] - use full path names when matching\n"
64 " [R] - recurse through directories when inserting\n"
65 " [s] - create an archive index (cf. ranlib)\n"
66 " [S] - do not build a symbol table\n"
67 " [u] - update only files newer than archive contents\n"
68 " [z] - compress files before inserting/extracting\n"
69 "\nMODIFIERS (generic):\n"
70 " [c] - do not warn if the library had to be created\n"
71 " [v] - be verbose about actions taken\n"
72 " [V] - be *really* verbose about actions taken\n"
75 // This enumeration delineates the kinds of operations on an archive
76 // that are permitted.
77 enum ArchiveOperation
{
78 NoOperation
, ///< An operation hasn't been specified
79 Print
, ///< Print the contents of the archive
80 Delete
, ///< Delete the specified members
81 Move
, ///< Move members to end or as given by {a,b,i} modifiers
82 QuickAppend
, ///< Quickly append to end of archive
83 ReplaceOrInsert
, ///< Replace or Insert members
84 DisplayTable
, ///< Display the table of contents
85 Extract
///< Extract files back to file system
88 // Modifiers to follow operation to vary behavior
89 bool AddAfter
= false; ///< 'a' modifier
90 bool AddBefore
= false; ///< 'b' modifier
91 bool Create
= false; ///< 'c' modifier
92 bool TruncateNames
= false; ///< 'f' modifier
93 bool InsertBefore
= false; ///< 'i' modifier
94 bool DontSkipBitcode
= false; ///< 'k' modifier
95 bool UseCount
= false; ///< 'N' modifier
96 bool OriginalDates
= false; ///< 'o' modifier
97 bool FullPath
= false; ///< 'P' modifier
98 bool RecurseDirectories
= false; ///< 'R' modifier
99 bool SymTable
= true; ///< 's' & 'S' modifiers
100 bool OnlyUpdate
= false; ///< 'u' modifier
101 bool Verbose
= false; ///< 'v' modifier
102 bool ReallyVerbose
= false; ///< 'V' modifier
103 bool Compression
= false; ///< 'z' modifier
105 // Relative Positional Argument (for insert/move). This variable holds
106 // the name of the archive member to which the 'a', 'b' or 'i' modifier
107 // refers. Only one of 'a', 'b' or 'i' can be specified so we only need
111 // Select which of multiple entries in the archive with the same name should be
112 // used (specified with -N) for the delete and extract operations.
115 // This variable holds the name of the archive file as given on the
117 std::string ArchiveName
;
119 // This variable holds the list of member files to proecess, as given
120 // on the command line.
121 std::vector
<std::string
> Members
;
123 // This variable holds the (possibly expanded) list of path objects that
124 // correspond to files we will
125 std::set
<sys::Path
> Paths
;
127 // The Archive object to which all the editing operations will be sent.
128 Archive
* TheArchive
= 0;
130 // getRelPos - Extract the member filename from the command line for
131 // the [relpos] argument associated with a, b, and i modifiers
133 if(RestOfArgs
.size() > 0) {
134 RelPos
= RestOfArgs
[0];
135 RestOfArgs
.erase(RestOfArgs
.begin());
138 throw "Expected [relpos] for a, b, or i modifier";
141 // getCount - Extract the [count] argument associated with the N modifier
142 // from the command line and check its value.
144 if(RestOfArgs
.size() > 0) {
145 Count
= atoi(RestOfArgs
[0].c_str());
146 RestOfArgs
.erase(RestOfArgs
.begin());
149 throw "Expected [count] value with N modifier";
151 // Non-positive counts are not allowed
153 throw "Invalid [count] value (not a positive integer)";
156 // getArchive - Get the archive file name from the command line
158 if(RestOfArgs
.size() > 0) {
159 ArchiveName
= RestOfArgs
[0];
160 RestOfArgs
.erase(RestOfArgs
.begin());
163 throw "An archive name must be specified.";
166 // getMembers - Copy over remaining items in RestOfArgs to our Members vector
167 // This is just for clarity.
169 if(RestOfArgs
.size() > 0)
170 Members
= std::vector
<std::string
>(RestOfArgs
);
173 // parseCommandLine - Parse the command line options as presented and return the
174 // operation specified. Process all modifiers and check to make sure that
175 // constraints on modifier/operation pairs have not been violated.
176 ArchiveOperation
parseCommandLine() {
178 // Keep track of number of operations. We can only specify one
180 unsigned NumOperations
= 0;
182 // Keep track of the number of positional modifiers (a,b,i). Only
183 // one can be specified.
184 unsigned NumPositional
= 0;
186 // Keep track of which operation was requested
187 ArchiveOperation Operation
= NoOperation
;
189 for(unsigned i
=0; i
<Options
.size(); ++i
) {
191 case 'd': ++NumOperations
; Operation
= Delete
; break;
192 case 'm': ++NumOperations
; Operation
= Move
; break;
193 case 'p': ++NumOperations
; Operation
= Print
; break;
194 case 'q': ++NumOperations
; Operation
= QuickAppend
; break;
195 case 'r': ++NumOperations
; Operation
= ReplaceOrInsert
; break;
196 case 't': ++NumOperations
; Operation
= DisplayTable
; break;
197 case 'x': ++NumOperations
; Operation
= Extract
; break;
198 case 'c': Create
= true; break;
199 case 'f': TruncateNames
= true; break;
200 case 'k': DontSkipBitcode
= true; break;
201 case 'l': /* accepted but unused */ break;
202 case 'o': OriginalDates
= true; break;
203 case 'P': FullPath
= true; break;
204 case 'R': RecurseDirectories
= true; break;
205 case 's': SymTable
= true; break;
206 case 'S': SymTable
= false; break;
207 case 'u': OnlyUpdate
= true; break;
208 case 'v': Verbose
= true; break;
209 case 'V': Verbose
= ReallyVerbose
= true; break;
210 case 'z': Compression
= true; break;
231 cl::PrintHelpMessage();
235 // At this point, the next thing on the command line must be
239 // Everything on the command line at this point is a member.
242 // Perform various checks on the operation/modifier specification
243 // to make sure we are dealing with a legal request.
244 if (NumOperations
== 0)
245 throw "You must specify at least one of the operations";
246 if (NumOperations
> 1)
247 throw "Only one operation may be specified";
248 if (NumPositional
> 1)
249 throw "You may only specify one of a, b, and i modifiers";
250 if (AddAfter
|| AddBefore
|| InsertBefore
)
251 if (Operation
!= Move
&& Operation
!= ReplaceOrInsert
)
252 throw "The 'a', 'b' and 'i' modifiers can only be specified with "
253 "the 'm' or 'r' operations";
254 if (RecurseDirectories
&& Operation
!= ReplaceOrInsert
)
255 throw "The 'R' modifiers is only applicabe to the 'r' operation";
256 if (OriginalDates
&& Operation
!= Extract
)
257 throw "The 'o' modifier is only applicable to the 'x' operation";
258 if (TruncateNames
&& Operation
!=QuickAppend
&& Operation
!=ReplaceOrInsert
)
259 throw "The 'f' modifier is only applicable to the 'q' and 'r' operations";
260 if (OnlyUpdate
&& Operation
!= ReplaceOrInsert
)
261 throw "The 'u' modifier is only applicable to the 'r' operation";
262 if (Compression
&& Operation
!=ReplaceOrInsert
&& Operation
!=Extract
)
263 throw "The 'z' modifier is only applicable to the 'r' and 'x' operations";
264 if (Count
> 1 && Members
.size() > 1)
265 throw "Only one member name may be specified with the 'N' modifier";
267 // Return the parsed operation to the caller
271 // recurseDirectories - Implements the "R" modifier. This function scans through
272 // the Paths vector (built by buildPaths, below) and replaces any directories it
273 // finds with all the files in that directory (recursively). It uses the
274 // sys::Path::getDirectoryContent method to perform the actual directory scans.
276 recurseDirectories(const sys::Path
& path
,
277 std::set
<sys::Path
>& result
, std::string
* ErrMsg
) {
279 if (RecurseDirectories
) {
280 std::set
<sys::Path
> content
;
281 if (path
.getDirectoryContents(content
, ErrMsg
))
284 for (std::set
<sys::Path
>::iterator I
= content
.begin(), E
= content
.end();
286 // Make sure it exists and is a directory
287 sys::PathWithStatus
PwS(*I
);
288 const sys::FileStatus
*Status
= PwS
.getFileStatus(false, ErrMsg
);
292 std::set
<sys::Path
> moreResults
;
293 if (recurseDirectories(*I
, moreResults
, ErrMsg
))
295 result
.insert(moreResults
.begin(), moreResults
.end());
304 // buildPaths - Convert the strings in the Members vector to sys::Path objects
305 // and make sure they are valid and exist exist. This check is only needed for
306 // the operations that add/replace files to the archive ('q' and 'r')
307 bool buildPaths(bool checkExistence
, std::string
* ErrMsg
) {
308 for (unsigned i
= 0; i
< Members
.size(); i
++) {
310 if (!aPath
.set(Members
[i
]))
311 throw std::string("File member name invalid: ") + Members
[i
];
312 if (checkExistence
) {
314 throw std::string("File does not exist: ") + Members
[i
];
316 sys::PathWithStatus
PwS(aPath
);
317 const sys::FileStatus
*si
= PwS
.getFileStatus(false, &Err
);
321 std::set
<sys::Path
> dirpaths
;
322 if (recurseDirectories(aPath
, dirpaths
, ErrMsg
))
324 Paths
.insert(dirpaths
.begin(),dirpaths
.end());
335 // printSymbolTable - print out the archive's symbol table.
336 void printSymbolTable() {
337 std::cout
<< "\nArchive Symbol Table:\n";
338 const Archive::SymTabType
& symtab
= TheArchive
->getSymbolTable();
339 for (Archive::SymTabType::const_iterator I
=symtab
.begin(), E
=symtab
.end();
341 unsigned offset
= TheArchive
->getFirstFileOffset() + I
->second
;
342 std::cout
<< " " << std::setw(9) << offset
<< "\t" << I
->first
<<"\n";
346 // doPrint - Implements the 'p' operation. This function traverses the archive
347 // looking for members that match the path list. It is careful to uncompress
348 // things that should be and to skip bitcode files unless the 'k' modifier was
350 bool doPrint(std::string
* ErrMsg
) {
351 if (buildPaths(false, ErrMsg
))
353 unsigned countDown
= Count
;
354 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
357 (std::find(Paths
.begin(), Paths
.end(), I
->getPath()) != Paths
.end())) {
358 if (countDown
== 1) {
359 const char* data
= reinterpret_cast<const char*>(I
->getData());
361 // Skip things that don't make sense to print
362 if (I
->isLLVMSymbolTable() || I
->isSVR4SymbolTable() ||
363 I
->isBSD4SymbolTable() || (!DontSkipBitcode
&& I
->isBitcode()))
367 std::cout
<< "Printing " << I
->getPath().toString() << "\n";
369 unsigned len
= I
->getSize();
370 std::cout
.write(data
, len
);
379 // putMode - utility function for printing out the file mode when the 't'
380 // operation is in verbose mode.
382 printMode(unsigned mode
) {
397 // doDisplayTable - Implement the 't' operation. This function prints out just
398 // the file names of each of the members. However, if verbose mode is requested
399 // ('v' modifier) then the file type, permission mode, user, group, size, and
400 // modification time are also printed.
402 doDisplayTable(std::string
* ErrMsg
) {
403 if (buildPaths(false, ErrMsg
))
405 for (Archive::iterator I
= TheArchive
->begin(), E
= TheArchive
->end();
408 (std::find(Paths
.begin(), Paths
.end(), I
->getPath()) != Paths
.end())) {
410 // FIXME: Output should be this format:
411 // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile
414 else if (I
->isCompressed())
418 unsigned mode
= I
->getMode();
419 printMode((mode
>> 6) & 007);
420 printMode((mode
>> 3) & 007);
421 printMode(mode
& 007);
422 std::cout
<< " " << std::setw(4) << I
->getUser();
423 std::cout
<< "/" << std::setw(4) << I
->getGroup();
424 std::cout
<< " " << std::setw(8) << I
->getSize();
425 std::cout
<< " " << std::setw(20) <<
426 I
->getModTime().toString().substr(4);
427 std::cout
<< " " << I
->getPath().toString() << "\n";
429 std::cout
<< I
->getPath().toString() << "\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().toString()) {
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
->toString());
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().toString()) {
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().toString()))
666 else if (AddAfter
&& (RelPos
== I
->getPath().toString())) {
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
);
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
.toString() << "\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
<< "': "
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.