2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // Example application using the nested command line parser.
19 // Implements two commands: "cat" and "echo", which behave similarly to their
22 #include <folly/ScopeGuard.h>
23 #include <folly/String.h>
24 #include <folly/experimental/NestedCommandLineApp.h>
25 #include <folly/experimental/ProgramOptions.h>
27 namespace po = ::boost::program_options;
31 class InputError : public std::runtime_error {
33 explicit InputError(const std::string& msg)
34 : std::runtime_error(msg) { }
37 class OutputError : public std::runtime_error {
39 explicit OutputError(const std::string& msg)
40 : std::runtime_error(msg) { }
45 explicit Concatenator(const po::variables_map& options)
46 : printLineNumbers_(options["number"].as<bool>()) { }
48 void cat(const std::string& name);
51 bool printLineNumbers() const { return printLineNumbers_; }
54 bool printLineNumbers_;
55 size_t lineNumber_ = 0;
58 [[noreturn]] void throwOutputError() {
59 throw OutputError(folly::errnoStr(errno).toStdString());
62 [[noreturn]] void throwInputError() {
63 throw InputError(folly::errnoStr(errno).toStdString());
66 void Concatenator::cat(FILE* file) {
67 char* lineBuf = nullptr;
68 size_t lineBufSize = 0;
74 while ((n = getline(&lineBuf, &lineBufSize, file)) >= 0) {
76 if ((printLineNumbers_ && printf("%6zu ", lineNumber_) < 0) ||
77 fwrite(lineBuf, 1, n, stdout) < size_t(n)) {
87 void Concatenator::cat(const std::string& name) {
88 auto file = fopen(name.c_str(), "r");
93 // Ignore error, as we might be processing an exception;
94 // during normal operation, we call fclose() directly further below
95 auto guard = folly::makeGuard([file] { fclose(file); });
105 void runCat(const po::variables_map& options,
106 const std::vector<std::string>& args) {
107 Concatenator concatenator(options);
109 auto catFile = [&concatenator, &ok] (const std::string& name) {
112 concatenator.cat(stdin);
114 concatenator.cat(name);
116 } catch (const InputError& e) {
118 fprintf(stderr, "cat: %s: %s\n", name.c_str(), e.what());
126 for (auto& name : args) {
130 } catch (const OutputError& e) {
131 throw folly::ProgramExit(
132 1, folly::to<std::string>("cat: write error: ", e.what()));
135 throw folly::ProgramExit(1);
139 void runEcho(const po::variables_map& options,
140 const std::vector<std::string>& args) {
142 const char* sep = "";
143 for (auto& arg : args) {
144 if (printf("%s%s", sep, arg.c_str()) < 0) {
145 throw OutputError(folly::errnoStr(errno).toStdString());
149 if (!options["-n"].as<bool>()) {
150 if (putchar('\n') == EOF) {
151 throw OutputError(folly::errnoStr(errno).toStdString());
154 } catch (const OutputError& e) {
155 throw folly::ProgramExit(
156 1, folly::to<std::string>("echo: write error: ", e.what()));
162 int main(int argc, char *argv[]) {
163 // Initialize a NestedCommandLineApp object.
165 // The first argument is the program name -- an empty string will cause the
166 // program name to be deduced from the executable name, which is usually
167 // fine. The second argument is a version string.
169 // You may also add an "initialization function" that is always called
170 // for every valid command before the command is executed.
171 folly::NestedCommandLineApp app("", "0.1");
173 // Add any GFlags-defined flags. These are global flags, and so they should
174 // be valid for any command.
177 // Add any commands. For our example, we'll implement simplified versions
178 // of "cat" and "echo". Note that addCommand() returns a reference to a
179 // boost::program_options object that you may use to add command-specific
185 // argument description
189 "Concatenate files and print them on standard output",
192 "Concatenate files and print them on standard output.",
194 // Function to execute
197 ("number,n", po::bool_switch(), "number all output lines");
202 "Display a line of text",
203 "Display a line of text.",
206 (",n", po::bool_switch(), "do not output the trailing newline");
208 // You may also add command aliases -- that is, multiple command names
209 // that do the same thing; see addAlias().
211 // app.run returns an appropriate error code
212 return app.run(argc, argv);