// -*- Mode: C++ -*- // // Copyright (C) 2013-2015 Red Hat, Inc. // // This file is part of the GNU Application Binary Interface Generic // Analysis and Instrumentation Library (libabigail). This library is // free software; you can redistribute it and/or modify it under the // terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 3, or (at your option) any // later version. // This library is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Lesser Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this program; see the file COPYING-LGPLV3. If // not, see . #include #include // The two above is for 'stat' #include // for 'strcmp' #include // for 'system' #include #include #include #include "abg-corpus.h" #include "abg-reader.h" #include "abg-writer.h" #include "abg-tools-utils.h" using std::cerr; using std::cout; using std::ostream; using std::string; using std::list; struct options { bool list_content; string extract_dest; string archive; list in_files; string out_dir; options() : list_content(false) { } };//end options /// Display a message explaining the usage of the program on stdout. static void display_usage(const string &prog_name, ostream& out) { out << "usage: " << prog_name << " [options] [archive-file-path]\n" << " where options are: \n" << "--help|-h display this usage message\n" << "--list|l list the archive content\n" << "--add|-a add files to an archive\n" << "--extract|x [dest-dir] extract archive content\n" ; } /// Parse the command line arguments and populate an instance of \a /// options as a result. /// /// @param argc the number of words on the command line, including the /// program name. /// /// @param argv an array of words representing the command line. This /// is the thing that is parsed. /// /// @param opts the options that are set as a result of parsing the /// command line. This output parameter is set iff the function /// returs true. /// /// @return true if the function could make sense of the command line /// and parsed it correctly. Otherwise, return false. If this /// function returns false, the caller would be well inspired to call /// display_usage() to remind the user how to properly invoke this /// program so that its command line makes sense. static bool parse_args(int argc, char* argv[], options& opts) { if (argc < 2) return false; for (int i = 1; i < argc; ++i) { char* arg = argv[i]; if (! strcmp(arg, "--help") ||! strcmp(arg, "-h")) return false; if (arg[0] != '-') opts.archive = arg; else if (! strcmp(arg, "--list") ||! strcmp(arg, "-l")) opts.list_content = true; else if (! strcmp(arg, "--add") || ! strcmp(arg,"-a")) { int arg_index0, arg_index1; char *f = 0; for (arg_index0 = i + 1; arg_index0 < argc; ++arg_index0) { // --add must be followed by at N words that don't start // by '-' (N > 1). The first N-1 words are the // arguments of --add (the files to add to the archive) // and the last one is the name of the archive to add // the files to. f = argv[arg_index0]; if (f[0] == '-') break; arg_index1 = arg_index0 + 1; if (arg_index1 >= argc || argv[arg_index1][0] == '-') break; opts.in_files.push_back(f); } if (opts.in_files.empty()) return false; } else if (! strcmp(arg, "--extract") || ! strcmp(arg, "-x")) { int arg_index = i + 1, arch_index = arg_index + 1; if (arg_index < argc && argv[arg_index][0] != '-' && arch_index < argc && argv[arch_index][0] != '-') opts.extract_dest = argv[arg_index]; else if (arg_index < argc && argv[arg_index][0] != '-') // No destination directory argument was given for the // --extract option, so consider it to be the current // directory opts.extract_dest = "."; else return false; } } return true; } using abigail::corpus; using abigail::corpus_sptr; using abigail::translation_unit; using abigail::translation_unit_sptr; using abigail::translation_units; using abigail::xml_reader::read_corpus_from_file; using abigail::xml_writer::write_corpus_to_archive; /// List the content of a given archive. The names of the files of /// the archive are then displayed on stdout. /// /// @param archive_path a path to the file containing the archive file /// to list the content of. /// /// @return true upon successful completion, false otherwise. static bool list_content(const string& archive_path) { if (archive_path.empty()) { cerr << "Empty archive path\n"; return false; } corpus_sptr archive = read_corpus_from_file(archive_path); if (!archive) { cerr << "Could not read archive at '" << archive_path << "'\n"; return false; } for (translation_units::const_iterator i = archive->get_translation_units().begin(); i != archive->get_translation_units().end(); ++i) cout << (*i)->get_path() << "\n"; return true; } /// Add a translation unit to an archive -- or create one for that /// matter. /// /// @param tu_paths a list of paths to add to the archive. /// /// @param archive_path the path of the archive to either open or /// create. The specified in \tu_paths are then added to this /// archive. Note that this function creates the entire directory /// tree needed up to \a archive_path, if needed. /// /// @return true upon successful completion, false otherwise. static bool add_tus_to_archive(const list& tu_paths, const string& archive_path) { translation_units tus; corpus corp(archive_path); bool added_some_tus = false; for (list::const_iterator i = tu_paths.begin(); i != tu_paths.end(); ++i) { translation_unit_sptr tu = abigail::xml_reader::read_translation_unit_from_file(*i); if (!tu) { cerr << "could not read binary instrumentation file '" << *i << "'\n"; continue; } corp.add(tu); added_some_tus = true; } if (added_some_tus) { if (!write_corpus_to_archive(corp)) { cerr << "could not write archive file '" << corp.get_path() << "'\n"; return false; } } return true; } /// Extract translation units from a given archive. /// /// @param dest_path the path of the destination directory which the /// elements of the archive are to be extracted under. /// /// @param archive_path the path to the archive to extract. The /// archive must exist and be accessible with read privileges. /// /// @return true upon successful completion, false otherwise. static bool extract_tus_from_archive(const string& dest_path, const string& archive_path) { if (dest_path.empty()) { cerr << "empty file directory\n"; return false; } corpus_sptr archive(new corpus(archive_path)); if (read_corpus_from_file(archive) < 1) { cerr << "could not read archive at '" << archive_path << "'\n;"; return false; } string cmd = "mkdir -p " + dest_path; if (system(cmd.c_str())) { cerr << "could not create file directory '" << dest_path << "'\n"; return false; } for (translation_units::const_iterator i = archive->get_translation_units().begin(); i != archive->get_translation_units().end(); ++i) { string dest = dest_path + "/" + (*i)->get_path(); if (!abigail::tools::ensure_parent_dir_created(dest)) { cerr << "could not create parent director for '" << dest << "'\n"; return false; } if (!abigail::xml_writer::write_translation_unit(**i, /*indent=*/0, dest)) { cerr << "could not write binary instrumentation file to '" << dest << "'\n"; return false; } } return true; } /// Parse the command line and perform the archive-related operations /// asked by the user, if the command line makes sense; otherwise, /// display a usage help message and bail out. int main(int argc, char* argv[]) { options opts; if (!parse_args(argc, argv, opts) || opts.archive.empty()) { display_usage(argv[0], cout); return -1; } if (opts.list_content) return !list_content(opts.archive); else if (!opts.in_files.empty()) return !add_tus_to_archive(opts.in_files, opts.archive); else if (!opts.extract_dest.empty()) return !extract_tus_from_archive(opts.extract_dest, opts.archive); else { display_usage(argv[0], cout); return -1; } return 0; }