llvm-ar.cpp revision 252ad03d7db0add504fdcc6bd67c1bc1e28bdd57
124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// 224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// 324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// The LLVM Compiler Infrastructure 424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// 524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// This file was developed by the LLVM research group and is distributed under 624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// the University of Illinois Open Source License. See LICENSE.TXT for details. 724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// 824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner//===----------------------------------------------------------------------===// 924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// 1024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// Builds up (relatively) standard unix archive files (.a) containing LLVM 11f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton// bytecode or other files. 12a408326b499c3ffdfed2378738598c4ad0cf745fEli Friedman// 1324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner//===----------------------------------------------------------------------===// 1424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 1524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include "llvm/Module.h" 1624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include "llvm/Bytecode/Archive.h" 1724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include "llvm/Support/CommandLine.h" 1824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include "llvm/Support/Compressor.h" 1924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include "llvm/System/Signals.h" 2024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include <iostream> 2124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include <algorithm> 2224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner#include <iomanip> 2300af72e395d138f459192b7f5450fa4c7854f135Greg Clayton#include <memory> 2424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 2524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerusing namespace llvm; 2624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 2724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// Option for compatibility with ASIX, not used but must allow it to be present. 2824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstatic cl::opt<bool> 2924943d2ee8bfaa7cf5893e4709143924157a5c1eChris LattnerX32Option ("X32_64", cl::Hidden, 3024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner cl::desc("Ignored option for compatibility with AIX")); 3124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 3224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// llvm-ar operation code and modifier flags. This must come first. 3324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstatic cl::opt<std::string> 3424943d2ee8bfaa7cf5893e4709143924157a5c1eChris LattnerOptions(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); 3597c8957257a3e0b3ce6f46f8e5a28c965e30f357Daniel Dunbar 3697c8957257a3e0b3ce6f46f8e5a28c965e30f357Daniel Dunbar// llvm-ar remaining positional arguments. 3724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstatic cl::list<std::string> 3824943d2ee8bfaa7cf5893e4709143924157a5c1eChris LattnerRestOfArgs(cl::Positional, cl::OneOrMore, 3903c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham cl::desc("[relpos] [count] <archive-file> [members]...")); 4024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 4124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// MoreHelp - Provide additional help output explaining the operations and 4224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// modifiers of llvm-ar. This object instructs the CommandLine library 4303c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham// to print the text of the constructor when the --help option is given. 4403c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghamstatic cl::extrahelp MoreHelp( 4503c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham "\nOPERATIONS:\n" 4603c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " d[NsS] - delete file(s) from the archive\n" 4703c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " m[abiSs] - move file(s) in the archive\n" 4803c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " p[kN] - print file(s) found in the archive\n" 4997c8957257a3e0b3ce6f46f8e5a28c965e30f357Daniel Dunbar " q[ufsS] - quick append file(s) to the archive\n" 5097c8957257a3e0b3ce6f46f8e5a28c965e30f357Daniel Dunbar " r[abfiuzRsS] - replace or insert file(s) into the archive\n" 5103c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " t - display contents of archive\n" 5203c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " x[No] - extract file(s) from the archive\n" 5303c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham "\nMODIFIERS (operation specific):\n" 5403c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [a] - put file(s) after [relpos]\n" 5503c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [b] - put file(s) before [relpos] (same as [i])\n" 5603c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [f] - truncate inserted file names\n" 5703c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [i] - put file(s) before [relpos] (same as [b])\n" 5803c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [k] - always print bytecode files (default is to skip them)\n" 5903c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [N] - use instance [count] of name\n" 6003c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [o] - preserve original dates\n" 6103c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [P] - use full path names when matching\n" 6203c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [R] - recurse through directories when inserting\n" 6303c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [s] - create an archive index (cf. ranlib)\n" 6403c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [S] - do not build a symbol table\n" 6503c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [u] - update only files newer than archive contents\n" 6603c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [z] - compress files before inserting/extracting\n" 6703c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham "\nMODIFIERS (generic):\n" 6803c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [c] - do not warn if the library had to be created\n" 6903c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [v] - be verbose about actions taken\n" 7003c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham " [V] - be *really* verbose about actions taken\n" 7103c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham); 7224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 7324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// This enumeration delineates the kinds of operations on an archive 7424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// that are permitted. 7524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerenum ArchiveOperation { 7624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner NoOperation, ///< An operation hasn't been specified 7724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner Print, ///< Print the contents of the archive 7824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner Delete, ///< Delete the specified members 7924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner Move, ///< Move members to end or as given by {a,b,i} modifiers 8024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner QuickAppend, ///< Quickly append to end of archive 8124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner ReplaceOrInsert, ///< Replace or Insert members 8224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner DisplayTable, ///< Display the table of contents 8324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner Extract ///< Extract files back to file system 8424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner}; 8524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 8624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// Modifiers to follow operation to vary behavior 8724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool AddAfter = false; ///< 'a' modifier 8824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool AddBefore = false; ///< 'b' modifier 8924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool Create = false; ///< 'c' modifier 9024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool TruncateNames = false; ///< 'f' modifier 9124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool InsertBefore = false; ///< 'i' modifier 9224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool DontSkipBytecode = false; ///< 'k' modifier 9324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool UseCount = false; ///< 'N' modifier 9424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool OriginalDates = false; ///< 'o' modifier 9503c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghambool FullPath = false; ///< 'P' modifier 9603c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghambool RecurseDirectories = false; ///< 'R' modifier 9703c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghambool SymTable = true; ///< 's' & 'S' modifiers 9803c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghambool OnlyUpdate = false; ///< 'u' modifier 9903c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghambool Verbose = false; ///< 'v' modifier 10003c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Inghambool ReallyVerbose = false; ///< 'V' modifier 10124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerbool Compression = false; ///< 'z' modifier 10224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 10324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// Relative Positional Argument (for insert/move). This variable holds 10403c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham// the name of the archive member to which the 'a', 'b' or 'i' modifier 10503c8ee5aeafcd6c43f10002a4f8096af01780f86Jim Ingham// refers. Only one of 'a', 'b' or 'i' can be specified so we only need 10624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// one variable. 10724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstd::string RelPos; 10824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 10924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// Select which of multiple entries in the archive with the same name should be 11024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// used (specified with -N) for the delete and extract operations. 11124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerint Count = 1; 11224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 11324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// This variable holds the name of the archive file as given on the 11424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// command line. 11524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstd::string ArchiveName; 11624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 11724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// This variable holds the list of member files to proecess, as given 11824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// on the command line. 11924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstd::vector<std::string> Members; 12024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 12124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// This variable holds the (possibly expanded) list of path objects that 12224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// correspond to files we will 12324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnerstd::set<sys::Path> Paths; 12424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 12524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// The Archive object to which all the editing operations will be sent. 12624943d2ee8bfaa7cf5893e4709143924157a5c1eChris LattnerArchive* TheArchive = 0; 12700af72e395d138f459192b7f5450fa4c7854f135Greg Clayton 12824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// getRelPos - Extract the member filename from the command line for 12900af72e395d138f459192b7f5450fa4c7854f135Greg Clayton// the [relpos] argument associated with a, b, and i modifiers 130f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Claytonvoid getRelPos() { 13100af72e395d138f459192b7f5450fa4c7854f135Greg Clayton if(RestOfArgs.size() > 0) { 132b1077c4b43d0e64654f4f02f97be0b7e16920570Greg Clayton RelPos = RestOfArgs[0]; 133f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton RestOfArgs.erase(RestOfArgs.begin()); 13400af72e395d138f459192b7f5450fa4c7854f135Greg Clayton } 13500af72e395d138f459192b7f5450fa4c7854f135Greg Clayton else 13600af72e395d138f459192b7f5450fa4c7854f135Greg Clayton throw "Expected [relpos] for a, b, or i modifier"; 137f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton} 13800af72e395d138f459192b7f5450fa4c7854f135Greg Clayton 13900af72e395d138f459192b7f5450fa4c7854f135Greg Clayton// getCount - Extract the [count] argument associated with the N modifier 14000af72e395d138f459192b7f5450fa4c7854f135Greg Clayton// from the command line and check its value. 14100af72e395d138f459192b7f5450fa4c7854f135Greg Claytonvoid getCount() { 14200af72e395d138f459192b7f5450fa4c7854f135Greg Clayton if(RestOfArgs.size() > 0) { 143f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton Count = atoi(RestOfArgs[0].c_str()); 14400af72e395d138f459192b7f5450fa4c7854f135Greg Clayton RestOfArgs.erase(RestOfArgs.begin()); 14500af72e395d138f459192b7f5450fa4c7854f135Greg Clayton } 14600af72e395d138f459192b7f5450fa4c7854f135Greg Clayton else 14700af72e395d138f459192b7f5450fa4c7854f135Greg Clayton throw "Expected [count] value with N modifier"; 14800af72e395d138f459192b7f5450fa4c7854f135Greg Clayton 149f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton // Non-positive counts are not allowed 150f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton if (Count < 1) 15100af72e395d138f459192b7f5450fa4c7854f135Greg Clayton throw "Invalid [count] value (not a positive integer)"; 15200af72e395d138f459192b7f5450fa4c7854f135Greg Clayton} 15300af72e395d138f459192b7f5450fa4c7854f135Greg Clayton 15400af72e395d138f459192b7f5450fa4c7854f135Greg Clayton// getArchive - Get the archive file name from the command line 15500af72e395d138f459192b7f5450fa4c7854f135Greg Claytonvoid getArchive() { 15600af72e395d138f459192b7f5450fa4c7854f135Greg Clayton if(RestOfArgs.size() > 0) { 15700af72e395d138f459192b7f5450fa4c7854f135Greg Clayton ArchiveName = RestOfArgs[0]; 15800af72e395d138f459192b7f5450fa4c7854f135Greg Clayton RestOfArgs.erase(RestOfArgs.begin()); 15900af72e395d138f459192b7f5450fa4c7854f135Greg Clayton } 160f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton else 161f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton throw "An archive name must be specified."; 162f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton} 16300af72e395d138f459192b7f5450fa4c7854f135Greg Clayton 16424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner// getMembers - Copy over remaining items in RestOfArgs to our Members vector 16500af72e395d138f459192b7f5450fa4c7854f135Greg Clayton// This is just for clarity. 16624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattnervoid getMembers() { 167928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton if(RestOfArgs.size() > 0) 168928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton Members = std::vector<std::string>(RestOfArgs); 169928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton} 170928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton 171928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton// parseCommandLine - Parse the command line options as presented and return the 172928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton// operation specified. Process all modifiers and check to make sure that 173928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton// constraints on modifier/operation pairs have not been violated. 174928d130789bd8ee38ce434ccb2d564e6069cf018Greg ClaytonArchiveOperation parseCommandLine() { 175928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton 176928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton // Keep track of number of operations. We can only specify one 177928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton // per execution. 178928d130789bd8ee38ce434ccb2d564e6069cf018Greg Clayton unsigned NumOperations = 0; 17924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 18024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // Keep track of the number of positional modifiers (a,b,i). Only 18124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // one can be specified. 18224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner unsigned NumPositional = 0; 183f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton 18400af72e395d138f459192b7f5450fa4c7854f135Greg Clayton // Keep track of which operation was requested 185f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton ArchiveOperation Operation = NoOperation; 18600af72e395d138f459192b7f5450fa4c7854f135Greg Clayton 187f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton for(unsigned i=0; i<Options.size(); ++i) { 188f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton switch(Options[i]) { 189f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'd': ++NumOperations; Operation = Delete; break; 190f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'm': ++NumOperations; Operation = Move ; break; 191f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'p': ++NumOperations; Operation = Print; break; 192f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'r': ++NumOperations; Operation = ReplaceOrInsert; break; 193f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 't': ++NumOperations; Operation = DisplayTable; break; 194f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'x': ++NumOperations; Operation = Extract; break; 195f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'c': Create = true; break; 196f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'f': TruncateNames = true; break; 197f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'k': DontSkipBytecode = true; break; 198f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'l': /* accepted but unused */ break; 199f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'o': OriginalDates = true; break; 200f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'P': FullPath = true; break; 201f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'R': RecurseDirectories = true; break; 202f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 's': SymTable = true; break; 203f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'S': SymTable = false; break; 20400af72e395d138f459192b7f5450fa4c7854f135Greg Clayton case 'u': OnlyUpdate = true; break; 205f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'v': Verbose = true; break; 20600af72e395d138f459192b7f5450fa4c7854f135Greg Clayton case 'V': Verbose = ReallyVerbose = true; break; 207f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'z': Compression = true; break; 208f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'a': 209f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton getRelPos(); 210f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton AddAfter = true; 211f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton NumPositional++; 212f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton break; 213f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'b': 214f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton getRelPos(); 215f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton AddBefore = true; 216f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton NumPositional++; 217f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton break; 218f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton case 'i': 219f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton getRelPos(); 220f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton InsertBefore = true; 221f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton NumPositional++; 222f892c42725ed36c97e8ce10e758170cf6f1aff83Greg Clayton break; 22324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner case 'N': 22424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner getCount(); 22524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner UseCount = true; 22624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner break; 22724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner default: 22824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner cl::PrintHelpMessage(); 22924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner } 23024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner } 23124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 23224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // At this point, the next thing on the command line must be 23324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // the archive name. 23424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner getArchive(); 23524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 23624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // Everything on the command line at this point is a member. 23724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner getMembers(); 23824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner 23924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // Perform various checks on the operation/modifier specification 24024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner // to make sure we are dealing with a legal request. 24173844aa19a7360b662e2be710fc3c969d6c86606Greg Clayton if (NumOperations == 0) 24273844aa19a7360b662e2be710fc3c969d6c86606Greg Clayton throw "You must specify at least one of the operations"; 24324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (NumOperations > 1) 24424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "Only one operation may be specified"; 24524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (NumPositional > 1) 24624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "You may only specify one of a, b, and i modifiers"; 24724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (AddAfter || AddBefore || InsertBefore) 24824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (Operation != Move && Operation != ReplaceOrInsert) 24924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "The 'a', 'b' and 'i' modifiers can only be specified with " 25024943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner "the 'm' or 'r' operations"; 25124943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (RecurseDirectories && Operation != ReplaceOrInsert) 25224943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "The 'R' modifiers is only applicabe to the 'r' operation"; 25324943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (OriginalDates && Operation != Extract) 25424943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "The 'o' modifier is only applicable to the 'x' operation"; 25524943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (TruncateNames && Operation!=QuickAppend && Operation!=ReplaceOrInsert) 25624943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "The 'f' modifier is only applicable to the 'q' and 'r' operations"; 25724943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (OnlyUpdate && Operation != ReplaceOrInsert) 25824943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner throw "The 'u' modifier is only applicable to the 'r' operation"; 25924943d2ee8bfaa7cf5893e4709143924157a5c1eChris Lattner if (Compression && Operation!=ReplaceOrInsert && Operation!=Extract) 2606bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton throw "The 'z' modifier is only applicable to the 'r' and 'x' operations"; 2616bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton if (Count > 1 && Members.size() > 1) 2626bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton throw "Only one member name may be specified with the 'N' modifier"; 2636bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton 2646bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton // Return the parsed operation to the caller 2656bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton return Operation; 2666bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton} 2676bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton 2686bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton// recurseDirectories - Implements the "R" modifier. This function scans through 2696bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton// the Paths vector (built by buildPaths, below) and replaces any directories it 2706bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton// finds with all the files in that directory (recursively). It uses the 2716bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton// sys::Path::getDirectoryContent method to perform the actual directory scans. 2726bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Claytonstd::set<sys::Path> recurseDirectories(const sys::Path& path) { 2736bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton assert(path.isDirectory() && "Oops, can't recurse a file"); 274886bc3e5cb48e9660692609a7be69ec15b898bd7Enrico Granata std::set<sys::Path> result; 275886bc3e5cb48e9660692609a7be69ec15b898bd7Enrico Granata if (RecurseDirectories) { 276886bc3e5cb48e9660692609a7be69ec15b898bd7Enrico Granata std::set<sys::Path> content; 277886bc3e5cb48e9660692609a7be69ec15b898bd7Enrico Granata path.getDirectoryContents(content); 278886bc3e5cb48e9660692609a7be69ec15b898bd7Enrico Granata for (std::set<sys::Path>::iterator I = content.begin(), E = content.end(); 2796bc0b5d69f6f5f46055be6cfea6f9a0eb11b1943Greg Clayton I != E; ++I) { 280 if (I->isDirectory()) { 281 std::set<sys::Path> moreResults = recurseDirectories(*I); 282 result.insert(moreResults.begin(), moreResults.end()); 283 } else { 284 result.insert(*I); 285 } 286 } 287 } 288 return result; 289} 290 291// buildPaths - Convert the strings in the Members vector to sys::Path objects 292// and make sure they are valid and exist exist. This check is only needed for 293// the operations that add/replace files to the archive ('q' and 'r') 294void buildPaths(bool checkExistence = true) { 295 for (unsigned i = 0; i < Members.size(); i++) { 296 sys::Path aPath; 297 if (!aPath.set(Members[i])) 298 throw std::string("File member name invalid: ") + Members[i]; 299 if (checkExistence) { 300 if (!aPath.exists()) 301 throw std::string("File does not exist: ") + Members[i]; 302 sys::FileStatus si; 303 std::string Err; 304 if (aPath.getFileStatus(si, &Err)) 305 throw Err; 306 if (si.isDir) { 307 std::set<sys::Path> dirpaths = recurseDirectories(aPath); 308 Paths.insert(dirpaths.begin(),dirpaths.end()); 309 } else { 310 Paths.insert(aPath); 311 } 312 } else { 313 Paths.insert(aPath); 314 } 315 } 316} 317 318// printSymbolTable - print out the archive's symbol table. 319void printSymbolTable() { 320 std::cout << "\nArchive Symbol Table:\n"; 321 const Archive::SymTabType& symtab = TheArchive->getSymbolTable(); 322 for (Archive::SymTabType::const_iterator I=symtab.begin(), E=symtab.end(); 323 I != E; ++I ) { 324 unsigned offset = TheArchive->getFirstFileOffset() + I->second; 325 std::cout << " " << std::setw(9) << offset << "\t" << I->first <<"\n"; 326 } 327} 328 329// doPrint - Implements the 'p' operation. This function traverses the archive 330// looking for members that match the path list. It is careful to uncompress 331// things that should be and to skip bytecode files unless the 'k' modifier was 332// given. 333void doPrint() { 334 buildPaths(false); 335 unsigned countDown = Count; 336 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 337 I != E; ++I ) { 338 if (Paths.empty() || 339 (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { 340 if (countDown == 1) { 341 const char* data = reinterpret_cast<const char*>(I->getData()); 342 343 // Skip things that don't make sense to print 344 if (I->isLLVMSymbolTable() || I->isSVR4SymbolTable() || 345 I->isBSD4SymbolTable() || (!DontSkipBytecode && 346 (I->isBytecode() || I->isCompressedBytecode()))) 347 continue; 348 349 if (Verbose) 350 std::cout << "Printing " << I->getPath().toString() << "\n"; 351 352 if (I->isCompressedBytecode()) 353 Compressor::decompressToStream(data+4,I->getSize()-4,std::cout); 354 else if (I->isCompressed()) { 355 Compressor::decompressToStream(data,I->getSize(),std::cout); 356 } else { 357 unsigned len = I->getSize(); 358 std::cout.write(data, len); 359 } 360 } else { 361 countDown--; 362 } 363 } 364 } 365} 366 367// putMode - utility function for printing out the file mode when the 't' 368// operation is in verbose mode. 369void printMode(unsigned mode) { 370 if (mode & 004) 371 std::cout << "r"; 372 else 373 std::cout << "-"; 374 if (mode & 002) 375 std::cout << "w"; 376 else 377 std::cout << "-"; 378 if (mode & 001) 379 std::cout << "x"; 380 else 381 std::cout << "-"; 382} 383 384// doDisplayTable - Implement the 't' operation. This function prints out just 385// the file names of each of the members. However, if verbose mode is requested 386// ('v' modifier) then the file type, permission mode, user, group, size, and 387// modification time are also printed. 388void doDisplayTable() { 389 buildPaths(false); 390 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 391 I != E; ++I ) { 392 if (Paths.empty() || 393 (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { 394 if (Verbose) { 395 // FIXME: Output should be this format: 396 // Zrw-r--r-- 500/ 500 525 Nov 8 17:42 2004 Makefile 397 if (I->isBytecode()) 398 std::cout << "b"; 399 else if (I->isCompressedBytecode()) 400 std::cout << "B"; 401 else if (I->isCompressed()) 402 std::cout << "Z"; 403 else 404 std::cout << " "; 405 unsigned mode = I->getMode(); 406 printMode((mode >> 6) & 007); 407 printMode((mode >> 3) & 007); 408 printMode(mode & 007); 409 std::cout << " " << std::setw(4) << I->getUser(); 410 std::cout << "/" << std::setw(4) << I->getGroup(); 411 std::cout << " " << std::setw(8) << I->getSize(); 412 std::cout << " " << std::setw(20) << 413 I->getModTime().toString().substr(4); 414 std::cout << " " << I->getPath().toString() << "\n"; 415 } else { 416 std::cout << I->getPath().toString() << "\n"; 417 } 418 } 419 } 420 if (ReallyVerbose) 421 printSymbolTable(); 422} 423 424// doExtract - Implement the 'x' operation. This function extracts files back to 425// the file system, making sure to uncompress any that were compressed. 426void doExtract() { 427 buildPaths(false); 428 unsigned countDown = Count; 429 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 430 I != E; ++I ) { 431 if (Paths.empty() || 432 (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { 433 434 // Make sure the intervening directories are created 435 if (I->hasPath()) { 436 sys::Path dirs(I->getPath()); 437 dirs.eraseComponent(); 438 dirs.createDirectoryOnDisk(/*create_parents=*/true); 439 } 440 441 // Open up a file stream for writing 442 std::ios::openmode io_mode = std::ios::out | std::ios::trunc | 443 std::ios::binary; 444 std::ofstream file(I->getPath().c_str(), io_mode); 445 446 // Get the data and its length 447 const char* data = reinterpret_cast<const char*>(I->getData()); 448 unsigned len = I->getSize(); 449 450 // Write the data, making sure to uncompress things first 451 if (I->isCompressed()) { 452 Compressor::decompressToStream(data,len,file); 453 } else { 454 file.write(data,len); 455 } 456 file.close(); 457 458 // If we're supposed to retain the original modification times, etc. do so 459 // now. 460 if (OriginalDates) 461 I->getPath().setStatusInfoOnDisk(I->getFileStatus()); 462 } 463 } 464} 465 466// doDelete - Implement the delete operation. This function deletes zero or more 467// members from the archive. Note that if the count is specified, there should 468// be no more than one path in the Paths list or else this algorithm breaks. 469// That check is enforced in parseCommandLine (above). 470void doDelete() { 471 buildPaths(false); 472 if (Paths.empty()) return; 473 unsigned countDown = Count; 474 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 475 I != E; ) { 476 if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) { 477 if (countDown == 1) { 478 Archive::iterator J = I; 479 ++I; 480 TheArchive->erase(J); 481 } else 482 countDown--; 483 } else { 484 ++I; 485 } 486 } 487 488 // We're done editting, reconstruct the archive. 489 std::string errmsg; 490 if (!TheArchive->writeToDisk(SymTable,TruncateNames,Compression,&errmsg)) 491 throw errmsg; 492 if (ReallyVerbose) 493 printSymbolTable(); 494} 495 496// doMore - Implement the move operation. This function re-arranges just the 497// order of the archive members so that when the archive is written the move 498// of the members is accomplished. Note the use of the RelPos variable to 499// determine where the items should be moved to. 500void doMove() { 501 502 buildPaths(false); 503 504 // By default and convention the place to move members to is the end of the 505 // archive. 506 Archive::iterator moveto_spot = TheArchive->end(); 507 508 // However, if the relative positioning modifiers were used, we need to scan 509 // the archive to find the member in question. If we don't find it, its no 510 // crime, we just move to the end. 511 if (AddBefore || InsertBefore || AddAfter) { 512 for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); 513 I != E; ++I ) { 514 if (RelPos == I->getPath().toString()) { 515 if (AddAfter) { 516 moveto_spot = I; 517 moveto_spot++; 518 } else { 519 moveto_spot = I; 520 } 521 break; 522 } 523 } 524 } 525 526 // Keep a list of the paths remaining to be moved 527 std::set<sys::Path> remaining(Paths); 528 529 // Scan the archive again, this time looking for the members to move to the 530 // moveto_spot. 531 for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); 532 I != E && !remaining.empty(); ++I ) { 533 std::set<sys::Path>::iterator found = 534 std::find(remaining.begin(),remaining.end(),I->getPath()); 535 if (found != remaining.end()) { 536 if (I != moveto_spot) 537 TheArchive->splice(moveto_spot,*TheArchive,I); 538 remaining.erase(found); 539 } 540 } 541 542 // We're done editting, reconstruct the archive. 543 std::string errmsg; 544 if (!TheArchive->writeToDisk(SymTable,TruncateNames,Compression,&errmsg)) 545 throw errmsg; 546 if (ReallyVerbose) 547 printSymbolTable(); 548} 549 550// doQuickAppend - Implements the 'q' operation. This function just 551// indiscriminantly adds the members to the archive and rebuilds it. 552void doQuickAppend() { 553 // Get the list of paths to append. 554 buildPaths(true); 555 if (Paths.empty()) return; 556 557 // Append them quickly. 558 for (std::set<sys::Path>::iterator PI = Paths.begin(), PE = Paths.end(); 559 PI != PE; ++PI) { 560 TheArchive->addFileBefore(*PI,TheArchive->end()); 561 } 562 563 // We're done editting, reconstruct the archive. 564 std::string errmsg; 565 if (!TheArchive->writeToDisk(SymTable,TruncateNames,Compression,&errmsg)) 566 throw errmsg; 567 if (ReallyVerbose) 568 printSymbolTable(); 569} 570 571// doReplaceOrInsert - Implements the 'r' operation. This function will replace 572// any existing files or insert new ones into the archive. 573void doReplaceOrInsert() { 574 575 // Build the list of files to be added/replaced. 576 buildPaths(true); 577 if (Paths.empty()) return; 578 579 // Keep track of the paths that remain to be inserted. 580 std::set<sys::Path> remaining(Paths); 581 582 // Default the insertion spot to the end of the archive 583 Archive::iterator insert_spot = TheArchive->end(); 584 585 // Iterate over the archive contents 586 for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); 587 I != E && !remaining.empty(); ++I ) { 588 589 // Determine if this archive member matches one of the paths we're trying 590 // to replace. 591 592 std::set<sys::Path>::iterator found = remaining.end(); 593 for (std::set<sys::Path>::iterator RI = remaining.begin(), 594 RE = remaining.end(); RI != RE; ++RI ) { 595 std::string compare(RI->toString()); 596 if (TruncateNames && compare.length() > 15) { 597 const char* nm = compare.c_str(); 598 unsigned len = compare.length(); 599 size_t slashpos = compare.rfind('/'); 600 if (slashpos != std::string::npos) { 601 nm += slashpos + 1; 602 len -= slashpos +1; 603 } 604 if (len > 15) 605 len = 15; 606 compare.assign(nm,len); 607 } 608 if (compare == I->getPath().toString()) { 609 found = RI; 610 break; 611 } 612 } 613 614 if (found != remaining.end()) { 615 sys::FileStatus si; 616 std::string Err; 617 if (found->getFileStatus(si, &Err)) 618 throw Err; 619 if (si.isDir) { 620 if (OnlyUpdate) { 621 // Replace the item only if it is newer. 622 if (si.modTime > I->getModTime()) 623 I->replaceWith(*found); 624 } else { 625 // Replace the item regardless of time stamp 626 I->replaceWith(*found); 627 } 628 } else { 629 // We purposefully ignore directories. 630 } 631 632 // Remove it from our "to do" list 633 remaining.erase(found); 634 } 635 636 // Determine if this is the place where we should insert 637 if ((AddBefore || InsertBefore) && (RelPos == I->getPath().toString())) 638 insert_spot = I; 639 else if (AddAfter && (RelPos == I->getPath().toString())) { 640 insert_spot = I; 641 insert_spot++; 642 } 643 } 644 645 // If we didn't replace all the members, some will remain and need to be 646 // inserted at the previously computed insert-spot. 647 if (!remaining.empty()) { 648 for (std::set<sys::Path>::iterator PI = remaining.begin(), 649 PE = remaining.end(); PI != PE; ++PI) { 650 TheArchive->addFileBefore(*PI,insert_spot); 651 } 652 } 653 654 // We're done editting, reconstruct the archive. 655 std::string errmsg; 656 if (!TheArchive->writeToDisk(SymTable,TruncateNames,Compression,&errmsg)) 657 throw errmsg; 658 if (ReallyVerbose) 659 printSymbolTable(); 660} 661 662// main - main program for llvm-ar .. see comments in the code 663int main(int argc, char **argv) { 664 665 // Have the command line options parsed and handle things 666 // like --help and --version. 667 cl::ParseCommandLineOptions(argc, argv, 668 " LLVM Archiver (llvm-ar)\n\n" 669 " This program archives bytecode files into single libraries\n" 670 ); 671 672 // Print a stack trace if we signal out. 673 sys::PrintStackTraceOnErrorSignal(); 674 675 int exitCode = 0; 676 677 // Make sure we don't exit with "unhandled exception". 678 try { 679 // Do our own parsing of the command line because the CommandLine utility 680 // can't handle the grouped positional parameters without a dash. 681 ArchiveOperation Operation = parseCommandLine(); 682 683 // Check the path name of the archive 684 sys::Path ArchivePath; 685 if (!ArchivePath.set(ArchiveName)) 686 throw std::string("Archive name invalid: ") + ArchiveName; 687 688 // Create or open the archive object. 689 if (!ArchivePath.exists()) { 690 // Produce a warning if we should and we're creating the archive 691 if (!Create) 692 std::cerr << argv[0] << ": creating " << ArchivePath.toString() << "\n"; 693 TheArchive = Archive::CreateEmpty(ArchivePath); 694 } else { 695 std::string Error; 696 TheArchive = Archive::OpenAndLoad(ArchivePath, &Error); 697 if (TheArchive == 0) { 698 std::cerr << argv[0] << ": error loading '" << ArchivePath << "': " 699 << Error << "!\n"; 700 return 1; 701 } 702 } 703 704 // Make sure we're not fooling ourselves. 705 assert(TheArchive && "Unable to instantiate the archive"); 706 707 // Make sure we clean up the archive even on failure. 708 std::auto_ptr<Archive> AutoArchive(TheArchive); 709 710 // Perform the operation 711 switch (Operation) { 712 case Print: doPrint(); break; 713 case Delete: doDelete(); break; 714 case Move: doMove(); break; 715 case QuickAppend: /* FALL THROUGH */ 716 case ReplaceOrInsert: doReplaceOrInsert(); break; 717 case DisplayTable: doDisplayTable(); break; 718 case Extract: doExtract(); break; 719 case NoOperation: 720 std::cerr << argv[0] << ": No operation was selected.\n"; 721 break; 722 } 723 } catch (const char*msg) { 724 // These errors are usage errors, thrown only by the various checks in the 725 // code above. 726 std::cerr << argv[0] << ": " << msg << "\n\n"; 727 cl::PrintHelpMessage(); 728 exitCode = 1; 729 } catch (const std::string& msg) { 730 // These errors are thrown by LLVM libraries (e.g. lib System) and represent 731 // a more serious error so we bump the exitCode and don't print the usage. 732 std::cerr << argv[0] << ": " << msg << "\n"; 733 exitCode = 2; 734 } catch (...) { 735 // This really shouldn't happen, but just in case .... 736 std::cerr << argv[0] << ": An unexpected unknown exception occurred.\n"; 737 exitCode = 3; 738 } 739 740 // Return result code back to operating system. 741 return exitCode; 742} 743