193 lines
6.0 KiB
C++
193 lines
6.0 KiB
C++
/* -*- 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);
|
|
}
|