X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=tools%2Fllvm-ar%2Fllvm-ar.cpp;h=a6611a3e8091c75ef36c11cc3b92f5fe8bf5d466;hb=73b43b9b549a75fb0015c825df68abd95705a67c;hp=1a4d75f46d8beade0f88d8f55382186ff2449b54;hpb=bede58363c647a4e73112fdfb409e9af7051301e;p=oota-llvm.git diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index 1a4d75f46d8..a6611a3e809 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -1,48 +1,47 @@ //===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// -// +// // The LLVM Compiler Infrastructure // -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// //===----------------------------------------------------------------------===// // -// Builds up (relatively) standard unix archive files (.a) containing LLVM -// bytecode or other files. +// Builds up (relatively) standard unix archive files (.a) containing LLVM +// bitcode or other files. // //===----------------------------------------------------------------------===// #include "llvm/Module.h" -#include "llvm/Bytecode/Archive.h" +#include "llvm/Bitcode/Archive.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compressor.h" -#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/ManagedStatic.h" #include "llvm/System/Signals.h" #include #include #include - +#include using namespace llvm; -// Option for compatibility with ASIX, not used but must allow it to be present. -static cl::opt -X32Option ("X32_64", cl::Hidden, +// Option for compatibility with AIX, not used but must allow it to be present. +static cl::opt +X32Option ("X32_64", cl::Hidden, cl::desc("Ignored option for compatibility with AIX")); // llvm-ar operation code and modifier flags. This must come first. -static cl::opt +static cl::opt Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); // llvm-ar remaining positional arguments. -static cl::list -RestOfArgs(cl::Positional, cl::OneOrMore, +static cl::list +RestOfArgs(cl::Positional, cl::OneOrMore, cl::desc("[relpos] [count] [members]...")); // MoreHelp - Provide additional help output explaining the operations and // modifiers of llvm-ar. This object instructs the CommandLine library // to print the text of the constructor when the --help option is given. static cl::extrahelp MoreHelp( - "\nOPERATIONS:\n" + "\nOPERATIONS:\n" " d[NsS] - delete file(s) from the archive\n" " m[abiSs] - move file(s) in the archive\n" " p[kN] - print file(s) found in the archive\n" @@ -55,7 +54,7 @@ static cl::extrahelp MoreHelp( " [b] - put file(s) before [relpos] (same as [i])\n" " [f] - truncate inserted file names\n" " [i] - put file(s) before [relpos] (same as [b])\n" - " [k] - always print bytecode files (default is to skip them)\n" + " [k] - always print bitcode files (default is to skip them)\n" " [N] - use instance [count] of name\n" " [o] - preserve original dates\n" " [P] - use full path names when matching\n" @@ -80,16 +79,16 @@ enum ArchiveOperation { QuickAppend, ///< Quickly append to end of archive ReplaceOrInsert, ///< Replace or Insert members DisplayTable, ///< Display the table of contents - Extract, ///< Extract files back to file system + Extract ///< Extract files back to file system }; // Modifiers to follow operation to vary behavior bool AddAfter = false; ///< 'a' modifier bool AddBefore = false; ///< 'b' modifier -bool Create = false; ///< 'c' modifier +bool Create = false; ///< 'c' modifier bool TruncateNames = false; ///< 'f' modifier bool InsertBefore = false; ///< 'i' modifier -bool DontSkipBytecode = false; ///< 'k' modifier +bool DontSkipBitcode = false; ///< 'k' modifier bool UseCount = false; ///< 'N' modifier bool OriginalDates = false; ///< 'o' modifier bool FullPath = false; ///< 'P' modifier @@ -136,7 +135,7 @@ void getRelPos() { throw "Expected [relpos] for a, b, or i modifier"; } -// getCount - Extract the [count] argument associated with the N modifier +// getCount - Extract the [count] argument associated with the N modifier // from the command line and check its value. void getCount() { if(RestOfArgs.size() > 0) { @@ -165,11 +164,11 @@ void getArchive() { // This is just for clarity. void getMembers() { if(RestOfArgs.size() > 0) - Members = std::vector(RestOfArgs); + Members = std::vector(RestOfArgs); } // parseCommandLine - Parse the command line options as presented and return the -// operation specified. Process all modifiers and check to make sure that +// operation specified. Process all modifiers and check to make sure that // constraints on modifier/operation pairs have not been violated. ArchiveOperation parseCommandLine() { @@ -189,12 +188,13 @@ ArchiveOperation parseCommandLine() { case 'd': ++NumOperations; Operation = Delete; break; case 'm': ++NumOperations; Operation = Move ; break; case 'p': ++NumOperations; Operation = Print; break; - case 'r': ++NumOperations; Operation = ReplaceOrInsert; break; + case 'q': ++NumOperations; Operation = QuickAppend; break; + case 'r': ++NumOperations; Operation = ReplaceOrInsert; break; case 't': ++NumOperations; Operation = DisplayTable; break; case 'x': ++NumOperations; Operation = Extract; break; case 'c': Create = true; break; case 'f': TruncateNames = true; break; - case 'k': DontSkipBytecode = true; break; + case 'k': DontSkipBitcode = true; break; case 'l': /* accepted but unused */ break; case 'o': OriginalDates = true; break; case 'P': FullPath = true; break; @@ -221,7 +221,7 @@ ArchiveOperation parseCommandLine() { NumPositional++; break; case 'N': - getCount(); + getCount(); UseCount = true; break; default: @@ -229,7 +229,7 @@ ArchiveOperation parseCommandLine() { } } - // At this point, the next thing on the command line must be + // At this point, the next thing on the command line must be // the archive name. getArchive(); @@ -269,40 +269,55 @@ ArchiveOperation parseCommandLine() { // the Paths vector (built by buildPaths, below) and replaces any directories it // finds with all the files in that directory (recursively). It uses the // sys::Path::getDirectoryContent method to perform the actual directory scans. -std::set recurseDirectories(const sys::Path& path) { - assert(path.isDirectory() && "Oops, can't recurse a file"); - std::set result; +bool +recurseDirectories(const sys::Path& path, + std::set& result, std::string* ErrMsg) { + result.clear(); if (RecurseDirectories) { std::set content; - path.getDirectoryContents(content); - for (std::set::iterator I = content.begin(), E = content.end(); + if (path.getDirectoryContents(content, ErrMsg)) + return true; + + for (std::set::iterator I = content.begin(), E = content.end(); I != E; ++I) { - if (I->isDirectory()) { - std::set moreResults = recurseDirectories(*I); + // Make sure it exists and is a directory + sys::PathWithStatus PwS(*I); + const sys::FileStatus *Status = PwS.getFileStatus(false, ErrMsg); + if (!Status) + return true; + if (Status->isDir) { + std::set moreResults; + if (recurseDirectories(*I, moreResults, ErrMsg)) + return true; result.insert(moreResults.begin(), moreResults.end()); } else { - result.insert(*I); + result.insert(*I); } } } - return result; + return false; } // buildPaths - Convert the strings in the Members vector to sys::Path objects -// and make sure they are valid and exist exist. This check is only needed for +// and make sure they are valid and exist exist. This check is only needed for // the operations that add/replace files to the archive ('q' and 'r') -void buildPaths(bool checkExistence = true) { +bool buildPaths(bool checkExistence, std::string* ErrMsg) { for (unsigned i = 0; i < Members.size(); i++) { sys::Path aPath; - if (!aPath.setFile(Members[i])) + if (!aPath.set(Members[i])) throw std::string("File member name invalid: ") + Members[i]; if (checkExistence) { if (!aPath.exists()) throw std::string("File does not exist: ") + Members[i]; - sys::Path::StatusInfo si; - aPath.getStatusInfo(si); - if (si.isDir) { - std::set dirpaths = recurseDirectories(aPath); + std::string Err; + sys::PathWithStatus PwS(aPath); + const sys::FileStatus *si = PwS.getFileStatus(false, &Err); + if (!si) + throw Err; + if (si->isDir) { + std::set dirpaths; + if (recurseDirectories(aPath, dirpaths, ErrMsg)) + return true; Paths.insert(dirpaths.begin(),dirpaths.end()); } else { Paths.insert(aPath); @@ -311,13 +326,14 @@ void buildPaths(bool checkExistence = true) { Paths.insert(aPath); } } + return false; } // printSymbolTable - print out the archive's symbol table. void printSymbolTable() { std::cout << "\nArchive Symbol Table:\n"; const Archive::SymTabType& symtab = TheArchive->getSymbolTable(); - for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end(); + for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end(); I != E; ++I ) { unsigned offset = TheArchive->getFirstFileOffset() + I->second; std::cout << " " << std::setw(9) << offset << "\t" << I->first <<"\n"; @@ -326,46 +342,42 @@ void printSymbolTable() { // doPrint - Implements the 'p' operation. This function traverses the archive // looking for members that match the path list. It is careful to uncompress -// things that should be and to skip bytecode files unless the 'k' modifier was +// things that should be and to skip bitcode files unless the 'k' modifier was // given. -void doPrint() { - buildPaths(false); +bool doPrint(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; unsigned countDown = Count; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); I != E; ++I ) { - if (Paths.empty() || + if (Paths.empty() || (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { if (countDown == 1) { const char* data = reinterpret_cast(I->getData()); // Skip things that don't make sense to print - if (I->isLLVMSymbolTable() || I->isForeignSymbolTable() || - (!DontSkipBytecode && - (I->isBytecode() || I->isCompressedBytecode()))) + if (I->isLLVMSymbolTable() || I->isSVR4SymbolTable() || + I->isBSD4SymbolTable() || (!DontSkipBitcode && I->isBitcode())) continue; if (Verbose) - std::cout << "Printing " << I->getPath().get() << "\n"; + std::cout << "Printing " << I->getPath().toString() << "\n"; - if (I->isCompressedBytecode()) - Compressor::decompressToStream(data+4,I->getSize()-4,std::cout); - else if (I->isCompressed()) { - Compressor::decompressToStream(data,I->getSize(),std::cout); - } else { - unsigned len = I->getSize(); - std::cout.write(data, len); - } + unsigned len = I->getSize(); + std::cout.write(data, len); } else { countDown--; } } } + return false; } // putMode - utility function for printing out the file mode when the 't' // operation is in verbose mode. -void printMode(unsigned mode) { - if (mode & 004) +void +printMode(unsigned mode) { + if (mode & 004) std::cout << "r"; else std::cout << "-"; @@ -383,23 +395,19 @@ void printMode(unsigned mode) { // the file names of each of the members. However, if verbose mode is requested // ('v' modifier) then the file type, permission mode, user, group, size, and // modification time are also printed. -void doDisplayTable() { - buildPaths(false); - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); +bool +doDisplayTable(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); I != E; ++I ) { - if (Paths.empty() || + if (Paths.empty() || (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { if (Verbose) { // FIXME: Output should be this format: // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile - if (I->isBytecode()) + if (I->isBitcode()) std::cout << "b"; - else if (I->isCompressedBytecode()) - std::cout << "B"; - else if (I->isForeignSymbolTable()) - std::cout << "s"; - else if (I->isLLVMSymbolTable()) - std::cout << "S"; else if (I->isCompressed()) std::cout << "Z"; else @@ -411,24 +419,26 @@ void doDisplayTable() { std::cout << " " << std::setw(4) << I->getUser(); std::cout << "/" << std::setw(4) << I->getGroup(); std::cout << " " << std::setw(8) << I->getSize(); - std::cout << " " << std::setw(20) << + std::cout << " " << std::setw(20) << I->getModTime().toString().substr(4); - std::cout << " " << I->getPath().get() << "\n"; + std::cout << " " << I->getPath().toString() << "\n"; } else { - std::cout << I->getPath().get() << "\n"; + std::cout << I->getPath().toString() << "\n"; } } } if (ReallyVerbose) printSymbolTable(); + return false; } // doExtract - Implement the 'x' operation. This function extracts files back to -// the file system, making sure to uncompress any that were compressed. -void doExtract() { - buildPaths(false); - unsigned countDown = Count; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); +// the file system, making sure to uncompress any that were compressed +bool +doExtract(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); I != E; ++I ) { if (Paths.empty() || (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { @@ -436,42 +446,45 @@ void doExtract() { // Make sure the intervening directories are created if (I->hasPath()) { sys::Path dirs(I->getPath()); - dirs.elideFile(); - dirs.createDirectory(/*create_parents=*/true); + dirs.eraseComponent(); + if (dirs.createDirectoryOnDisk(/*create_parents=*/true, ErrMsg)) + return true; } // Open up a file stream for writing - std::ofstream file(I->getPath().c_str()); + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream file(I->getPath().c_str(), io_mode); // Get the data and its length const char* data = reinterpret_cast(I->getData()); unsigned len = I->getSize(); - // Write the data, making sure to uncompress things first - if (I->isCompressed()) { - Compressor::decompressToStream(data,len,file); - } else { - file.write(data,len); - } + // Write the data. + file.write(data,len); file.close(); // If we're supposed to retain the original modification times, etc. do so // now. if (OriginalDates) - I->getPath().setStatusInfo(I->getStatusInfo()); + I->getPath().setStatusInfoOnDisk(I->getFileStatus()); } } + return false; } // doDelete - Implement the delete operation. This function deletes zero or more // members from the archive. Note that if the count is specified, there should // be no more than one path in the Paths list or else this algorithm breaks. // That check is enforced in parseCommandLine (above). -void doDelete() { - buildPaths(false); - if (Paths.empty()) return; +bool +doDelete(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; + if (Paths.empty()) + return false; unsigned countDown = Count; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); + for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); I != E; ) { if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) { if (countDown == 1) { @@ -486,18 +499,21 @@ void doDelete() { } // We're done editting, reconstruct the archive. - TheArchive->writeToDisk(SymTable,TruncateNames,Compression); + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; if (ReallyVerbose) printSymbolTable(); + return false; } // doMore - Implement the move operation. This function re-arranges just the // order of the archive members so that when the archive is written the move // of the members is accomplished. Note the use of the RelPos variable to // determine where the items should be moved to. -void doMove() { - - buildPaths(false); +bool +doMove(std::string* ErrMsg) { + if (buildPaths(false, ErrMsg)) + return true; // By default and convention the place to move members to is the end of the // archive. @@ -507,9 +523,9 @@ void doMove() { // the archive to find the member in question. If we don't find it, its no // crime, we just move to the end. if (AddBefore || InsertBefore || AddAfter) { - for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); + for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); I != E; ++I ) { - if (RelPos == I->getPath().get()) { + if (RelPos == I->getPath().toString()) { if (AddAfter) { moveto_spot = I; moveto_spot++; @@ -526,49 +542,60 @@ void doMove() { // Scan the archive again, this time looking for the members to move to the // moveto_spot. - for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); + for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); I != E && !remaining.empty(); ++I ) { - std::set::iterator found = + std::set::iterator found = std::find(remaining.begin(),remaining.end(),I->getPath()); if (found != remaining.end()) { - if (I != moveto_spot) + if (I != moveto_spot) TheArchive->splice(moveto_spot,*TheArchive,I); remaining.erase(found); } } // We're done editting, reconstruct the archive. - TheArchive->writeToDisk(SymTable,TruncateNames,Compression); + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; if (ReallyVerbose) printSymbolTable(); + return false; } // doQuickAppend - Implements the 'q' operation. This function just // indiscriminantly adds the members to the archive and rebuilds it. -void doQuickAppend() { +bool +doQuickAppend(std::string* ErrMsg) { // Get the list of paths to append. - buildPaths(true); - if (Paths.empty()) return; + if (buildPaths(true, ErrMsg)) + return true; + if (Paths.empty()) + return false; // Append them quickly. for (std::set::iterator PI = Paths.begin(), PE = Paths.end(); PI != PE; ++PI) { - TheArchive->addFileBefore(*PI,TheArchive->end()); + if (TheArchive->addFileBefore(*PI,TheArchive->end(),ErrMsg)) + return true; } // We're done editting, reconstruct the archive. - TheArchive->writeToDisk(SymTable,TruncateNames,Compression); + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; if (ReallyVerbose) printSymbolTable(); + return false; } // doReplaceOrInsert - Implements the 'r' operation. This function will replace -// any existing files or insert new ones into the archive. -void doReplaceOrInsert() { +// any existing files or insert new ones into the archive. +bool +doReplaceOrInsert(std::string* ErrMsg) { // Build the list of files to be added/replaced. - buildPaths(true); - if (Paths.empty()) return; + if (buildPaths(true, ErrMsg)) + return true; + if (Paths.empty()) + return false; // Keep track of the paths that remain to be inserted. std::set remaining(Paths); @@ -582,19 +609,45 @@ void doReplaceOrInsert() { // Determine if this archive member matches one of the paths we're trying // to replace. - std::set::iterator found = - std::find(remaining.begin(),remaining.end(), I->getPath()); + + std::set::iterator found = remaining.end(); + for (std::set::iterator RI = remaining.begin(), + RE = remaining.end(); RI != RE; ++RI ) { + std::string compare(RI->toString()); + if (TruncateNames && compare.length() > 15) { + const char* nm = compare.c_str(); + unsigned len = compare.length(); + size_t slashpos = compare.rfind('/'); + if (slashpos != std::string::npos) { + nm += slashpos + 1; + len -= slashpos +1; + } + if (len > 15) + len = 15; + compare.assign(nm,len); + } + if (compare == I->getPath().toString()) { + found = RI; + break; + } + } + if (found != remaining.end()) { - sys::Path::StatusInfo si; - found->getStatusInfo(si); - if (si.isDir) { + std::string Err; + sys::PathWithStatus PwS(*found); + const sys::FileStatus *si = PwS.getFileStatus(false, &Err); + if (!si) + return true; + if (si->isDir) { if (OnlyUpdate) { // Replace the item only if it is newer. - if (si.modTime > I->getModTime()) - I->replaceWith(*found); + if (si->modTime > I->getModTime()) + if (I->replaceWith(*found, ErrMsg)) + return true; } else { // Replace the item regardless of time stamp - I->replaceWith(*found); + if (I->replaceWith(*found, ErrMsg)) + return true; } } else { // We purposefully ignore directories. @@ -605,9 +658,9 @@ void doReplaceOrInsert() { } // Determine if this is the place where we should insert - if ((AddBefore || InsertBefore) && (RelPos == I->getPath().get())) + if ((AddBefore || InsertBefore) && (RelPos == I->getPath().toString())) insert_spot = I; - else if (AddAfter && (RelPos == I->getPath().get())) { + else if (AddAfter && (RelPos == I->getPath().toString())) { insert_spot = I; insert_spot++; } @@ -616,26 +669,30 @@ void doReplaceOrInsert() { // If we didn't replace all the members, some will remain and need to be // inserted at the previously computed insert-spot. if (!remaining.empty()) { - for (std::set::iterator PI = remaining.begin(), + for (std::set::iterator PI = remaining.begin(), PE = remaining.end(); PI != PE; ++PI) { - TheArchive->addFileBefore(*PI,insert_spot); + if (TheArchive->addFileBefore(*PI,insert_spot, ErrMsg)) + return true; } } // We're done editting, reconstruct the archive. - TheArchive->writeToDisk(SymTable,TruncateNames,Compression); + if (TheArchive->writeToDisk(SymTable,TruncateNames,Compression,ErrMsg)) + return true; if (ReallyVerbose) printSymbolTable(); + return false; } // main - main program for llvm-ar .. see comments in the code int main(int argc, char **argv) { + llvm_shutdown_obj X; // Call llvm_shutdown() on exit. // Have the command line options parsed and handle things // like --help and --version. cl::ParseCommandLineOptions(argc, argv, - " LLVM Archiver (llvm-ar)\n\n" - " This program archives bytecode files into single libraries\n" + "LLVM Archiver (llvm-ar)\n\n" + " This program archives bitcode files into single libraries\n" ); // Print a stack trace if we signal out. @@ -651,17 +708,24 @@ int main(int argc, char **argv) { // Check the path name of the archive sys::Path ArchivePath; - if (!ArchivePath.setFile(ArchiveName)) + if (!ArchivePath.set(ArchiveName)) throw std::string("Archive name invalid: ") + ArchiveName; // Create or open the archive object. if (!ArchivePath.exists()) { // Produce a warning if we should and we're creating the archive if (!Create) - std::cerr << argv[0] << ": creating " << ArchivePath.get() << "\n"; + std::cerr << argv[0] << ": creating " << ArchivePath.toString() << "\n"; TheArchive = Archive::CreateEmpty(ArchivePath); + TheArchive->writeToDisk(); } else { - TheArchive = Archive::OpenAndLoad(ArchivePath); + std::string Error; + TheArchive = Archive::OpenAndLoad(ArchivePath, &Error); + if (TheArchive == 0) { + std::cerr << argv[0] << ": error loading '" << ArchivePath << "': " + << Error << "!\n"; + return 1; + } } // Make sure we're not fooling ourselves. @@ -671,18 +735,24 @@ int main(int argc, char **argv) { std::auto_ptr AutoArchive(TheArchive); // Perform the operation + std::string ErrMsg; + bool haveError = false; switch (Operation) { - case Print: doPrint(); break; - case Delete: doDelete(); break; - case Move: doMove(); break; - case QuickAppend: /* FALL THROUGH */ - case ReplaceOrInsert: doReplaceOrInsert(); break; - case DisplayTable: doDisplayTable(); break; - case Extract: doExtract(); break; + case Print: haveError = doPrint(&ErrMsg); break; + case Delete: haveError = doDelete(&ErrMsg); break; + case Move: haveError = doMove(&ErrMsg); break; + case QuickAppend: haveError = doQuickAppend(&ErrMsg); break; + case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break; + case DisplayTable: haveError = doDisplayTable(&ErrMsg); break; + case Extract: haveError = doExtract(&ErrMsg); break; case NoOperation: std::cerr << argv[0] << ": No operation was selected.\n"; break; } + if (haveError) { + std::cerr << argv[0] << ": " << ErrMsg << "\n"; + return 1; + } } catch (const char*msg) { // These errors are usage errors, thrown only by the various checks in the // code above.