| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2014-2023, Regents of the University of California, |
| * Arizona Board of Regents, |
| * Colorado State University, |
| * University Pierre & Marie Curie, Sorbonne University, |
| * Washington University in St. Louis, |
| * Beijing Institute of Technology, |
| * The University of Memphis. |
| * |
| * This file is part of NFD (Named Data Networking Forwarding Daemon). |
| * See AUTHORS.md for complete list of NFD authors and contributors. |
| * |
| * NFD is free software: you can redistribute it and/or modify it under the terms |
| * of the GNU General Public License as published by the Free Software Foundation, |
| * either version 3 of the License, or (at your option) any later version. |
| * |
| * NFD 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 Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "command-parser.hpp" |
| #include "help.hpp" |
| |
| #include "core/version.hpp" |
| |
| #include <boost/tokenizer.hpp> |
| #include <fstream> |
| #include <iostream> |
| |
| namespace nfd::tools::nfdc { |
| |
| static int |
| main(int argc, char** argv) |
| { |
| std::vector<std::string> args(argv + 1, argv + argc); |
| |
| CommandParser parser; |
| registerCommands(parser); |
| |
| if (args.empty()) { |
| helpList(std::cout, parser); |
| return 0; |
| } |
| |
| if (args[0] == "-V" || args[0] == "--version") { |
| std::cout << NFD_VERSION_BUILD_STRING << std::endl; |
| return 0; |
| } |
| |
| struct Command |
| { |
| std::string noun, verb; |
| CommandArguments ca; |
| ExecuteCommand execute; |
| }; |
| |
| auto processLine = [&parser] (const std::vector<std::string>& line) -> Command { |
| try { |
| auto [noun, verb, ca, execute] = parser.parse(line, ParseMode::ONE_SHOT); |
| return {noun, verb, ca, execute}; |
| } |
| catch (const std::invalid_argument& e) { |
| int ret = help(std::cout, parser, line); |
| if (ret == 2) |
| std::cerr << e.what() << std::endl; |
| return {}; |
| } |
| }; |
| |
| std::list<Command> commands; |
| |
| if (args[0] == "-f" || args[0] == "--batch") { |
| if (args.size() != 2) { |
| std::cerr << "ERROR: Invalid command line arguments: " << args[0] << " should follow with batch-file." |
| << " Use -h for more detail." << std::endl; |
| return 2; |
| } |
| |
| auto processIstream = [&commands, &processLine] (std::istream& is, const std::string& inputFile) { |
| std::string line; |
| size_t lineCounter = 0; |
| while (std::getline(is, line)) { |
| ++lineCounter; |
| |
| auto hasEscapeSlash = [] (const std::string& str) { |
| auto count = std::count(str.rbegin(), str.rend(), '\\'); |
| return (count % 2) == 1; |
| }; |
| while (!line.empty() && hasEscapeSlash(line)) { |
| std::string extraLine; |
| const auto& hasMore = std::getline(is, extraLine); |
| ++lineCounter; |
| line = line.substr(0, line.size() - 1) + extraLine; |
| if (!hasMore) { |
| break; |
| } |
| } |
| boost::tokenizer<boost::escaped_list_separator<char>> tokenizer( |
| line, |
| boost::escaped_list_separator<char>("\\", " \t", "\"'")); |
| |
| auto firstNonEmptyToken = tokenizer.begin(); |
| while (firstNonEmptyToken != tokenizer.end() && firstNonEmptyToken->empty()) { |
| ++firstNonEmptyToken; |
| } |
| |
| // Ignore empty lines (or lines with just spaces) and lines that start with # |
| // Non empty lines with trailing comment are not allowed and may trigger syntax error |
| if (firstNonEmptyToken == tokenizer.end() || (*firstNonEmptyToken)[0] == '#') { |
| continue; |
| } |
| |
| std::vector<std::string> lineArgs; |
| std::copy_if(firstNonEmptyToken, tokenizer.end(), std::back_inserter(lineArgs), |
| [] (const auto& t) { return !t.empty(); }); |
| |
| auto cmd = processLine(lineArgs); |
| if (cmd.noun.empty()) { |
| std::cerr << " >> Syntax error on line " << lineCounter << " of the batch in " |
| << inputFile << std::endl; |
| return 2; // not exactly correct, but should be indication of an error, which already shown |
| } |
| commands.push_back(std::move(cmd)); |
| } |
| return 0; |
| }; |
| |
| if (args[1] == "-") { |
| auto retval = processIstream(std::cin, "standard input"); |
| if (retval != 0) { |
| return retval; |
| } |
| } |
| else { |
| std::ifstream iff(args[1]); |
| if (!iff) { |
| std::cerr << "ERROR: Could not open `" << args[1] << "` batch file " |
| << "(" << strerror(errno) << ")" << std::endl; |
| return 2; |
| } |
| auto retval = processIstream(iff, args[1]); |
| if (retval != 0) { |
| return retval; |
| } |
| } |
| } |
| else { |
| commands.push_back(processLine(args)); |
| } |
| |
| try { |
| ndn::Face face; |
| ndn::KeyChain keyChain; |
| ndn::nfd::Controller controller(face, keyChain); |
| size_t commandCounter = 0; |
| for (auto& command : commands) { |
| ++commandCounter; |
| ExecuteContext ctx{command.noun, command.verb, command.ca, 0, |
| std::cout, std::cerr, face, keyChain, controller}; |
| command.execute(ctx); |
| |
| if (ctx.exitCode != 0) { |
| if (commands.size() > 1) { |
| std::cerr << " >> Failed to execute command on line " << commandCounter |
| << " of the batch file " << args[1] << std::endl; |
| std::cerr << " Note that nfdc has executed all commands on previous lines and " |
| << "stopped processing at this line" << std::endl; |
| } |
| |
| return ctx.exitCode; |
| } |
| } |
| return 0; |
| } |
| catch (const std::exception& e) { |
| std::cerr << e.what() << std::endl; |
| return 1; |
| } |
| } |
| |
| } // namespace nfd::tools::nfdc |
| |
| int |
| main(int argc, char** argv) |
| { |
| return nfd::tools::nfdc::main(argc, argv); |
| } |