main.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2023, Regents of the University of California,
4  * Arizona Board of Regents,
5  * Colorado State University,
6  * University Pierre & Marie Curie, Sorbonne University,
7  * Washington University in St. Louis,
8  * Beijing Institute of Technology,
9  * The University of Memphis.
10  *
11  * This file is part of NFD (Named Data Networking Forwarding Daemon).
12  * See AUTHORS.md for complete list of NFD authors and contributors.
13  *
14  * NFD is free software: you can redistribute it and/or modify it under the terms
15  * of the GNU General Public License as published by the Free Software Foundation,
16  * either version 3 of the License, or (at your option) any later version.
17  *
18  * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20  * PURPOSE. See the GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along with
23  * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "command-parser.hpp"
27 #include "help.hpp"
28 
29 #include "core/version.hpp"
30 
31 #include <boost/tokenizer.hpp>
32 #include <fstream>
33 #include <iostream>
34 
35 namespace nfd::tools::nfdc {
36 
37 static int
38 main(int argc, char** argv)
39 {
40  std::vector<std::string> args(argv + 1, argv + argc);
41 
42  CommandParser parser;
43  registerCommands(parser);
44 
45  if (args.empty()) {
46  helpList(std::cout, parser);
47  return 0;
48  }
49 
50  if (args[0] == "-V" || args[0] == "--version") {
51  std::cout << NFD_VERSION_BUILD_STRING << std::endl;
52  return 0;
53  }
54 
55  struct Command
56  {
57  std::string noun, verb;
59  ExecuteCommand execute;
60  };
61 
62  auto processLine = [&parser] (const std::vector<std::string>& line) -> Command {
63  try {
64  auto [noun, verb, ca, execute] = parser.parse(line, ParseMode::ONE_SHOT);
65  return {noun, verb, ca, execute};
66  }
67  catch (const std::invalid_argument& e) {
68  int ret = help(std::cout, parser, line);
69  if (ret == 2)
70  std::cerr << e.what() << std::endl;
71  return {};
72  }
73  };
74 
75  std::list<Command> commands;
76 
77  if (args[0] == "-f" || args[0] == "--batch") {
78  if (args.size() != 2) {
79  std::cerr << "ERROR: Invalid command line arguments: " << args[0] << " should follow with batch-file."
80  << " Use -h for more detail." << std::endl;
81  return 2;
82  }
83 
84  auto processIstream = [&commands, &processLine] (std::istream& is, const std::string& inputFile) {
85  std::string line;
86  size_t lineCounter = 0;
87  while (std::getline(is, line)) {
88  ++lineCounter;
89 
90  auto hasEscapeSlash = [] (const std::string& str) {
91  auto count = std::count(str.rbegin(), str.rend(), '\\');
92  return (count % 2) == 1;
93  };
94  while (!line.empty() && hasEscapeSlash(line)) {
95  std::string extraLine;
96  const auto& hasMore = std::getline(is, extraLine);
97  ++lineCounter;
98  line = line.substr(0, line.size() - 1) + extraLine;
99  if (!hasMore) {
100  break;
101  }
102  }
103  boost::tokenizer<boost::escaped_list_separator<char>> tokenizer(
104  line,
105  boost::escaped_list_separator<char>("\\", " \t", "\"'"));
106 
107  auto firstNonEmptyToken = tokenizer.begin();
108  while (firstNonEmptyToken != tokenizer.end() && firstNonEmptyToken->empty()) {
109  ++firstNonEmptyToken;
110  }
111 
112  // Ignore empty lines (or lines with just spaces) and lines that start with #
113  // Non empty lines with trailing comment are not allowed and may trigger syntax error
114  if (firstNonEmptyToken == tokenizer.end() || (*firstNonEmptyToken)[0] == '#') {
115  continue;
116  }
117 
118  std::vector<std::string> lineArgs;
119  std::copy_if(firstNonEmptyToken, tokenizer.end(), std::back_inserter(lineArgs),
120  [] (const auto& t) { return !t.empty(); });
121 
122  auto cmd = processLine(lineArgs);
123  if (cmd.noun.empty()) {
124  std::cerr << " >> Syntax error on line " << lineCounter << " of the batch in "
125  << inputFile << std::endl;
126  return 2; // not exactly correct, but should be indication of an error, which already shown
127  }
128  commands.push_back(std::move(cmd));
129  }
130  return 0;
131  };
132 
133  if (args[1] == "-") {
134  auto retval = processIstream(std::cin, "standard input");
135  if (retval != 0) {
136  return retval;
137  }
138  }
139  else {
140  std::ifstream iff(args[1]);
141  if (!iff) {
142  std::cerr << "ERROR: Could not open `" << args[1] << "` batch file "
143  << "(" << strerror(errno) << ")" << std::endl;
144  return 2;
145  }
146  auto retval = processIstream(iff, args[1]);
147  if (retval != 0) {
148  return retval;
149  }
150  }
151  }
152  else {
153  commands.push_back(processLine(args));
154  }
155 
156  try {
157  ndn::Face face;
158  ndn::KeyChain keyChain;
159  ndn::nfd::Controller controller(face, keyChain);
160  size_t commandCounter = 0;
161  for (auto& command : commands) {
162  ++commandCounter;
163  ExecuteContext ctx{command.noun, command.verb, command.ca, 0,
164  std::cout, std::cerr, face, keyChain, controller};
165  command.execute(ctx);
166 
167  if (ctx.exitCode != 0) {
168  if (commands.size() > 1) {
169  std::cerr << " >> Failed to execute command on line " << commandCounter
170  << " of the batch file " << args[1] << std::endl;
171  std::cerr << " Note that nfdc has executed all commands on previous lines and "
172  << "stopped processing at this line" << std::endl;
173  }
174 
175  return ctx.exitCode;
176  }
177  }
178  return 0;
179  }
180  catch (const std::exception& e) {
181  std::cerr << e.what() << std::endl;
182  return 1;
183  }
184 }
185 
186 } // namespace nfd::tools::nfdc
187 
188 int
189 main(int argc, char** argv)
190 {
191  return nfd::tools::nfdc::main(argc, argv);
192 }
Contains named command arguments.
std::tuple< std::string, std::string, CommandArguments, ExecuteCommand > parse(const std::vector< std::string > &tokens, ParseMode mode) const
Parse a command line.
Context for command execution.
int main(int argc, char **argv)
Definition: main.cpp:250
void helpList(std::ostream &os, const CommandParser &parser, ParseMode mode, std::string_view noun)
Writes the list of available commands to a stream.
Definition: help.cpp:42
void registerCommands(CommandParser &parser)
static int main(int argc, char **argv)
Definition: main.cpp:38
std::function< void(ExecuteContext &)> ExecuteCommand
A function to execute a command.
int help(std::ostream &os, const CommandParser &parser, std::vector< std::string > args)
Tries to help the user, if requested on the command line.
Definition: help.cpp:78
const char NFD_VERSION_BUILD_STRING[]
NFD version string, including git commit information if NFD is build from a specific git commit.