Skip to content

Commit

Permalink
Add portable option parser.
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 0d4f7e2f5bec4826e1c12e3aa1aee642fcf6da07
  • Loading branch information
levlam committed Jun 17, 2020
1 parent 4bb6fe7 commit c46910d
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 143 deletions.
2 changes: 1 addition & 1 deletion td/telegram/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4331,7 +4331,7 @@ void main(int argc, char **argv) {
}
return std::string();
}(std::getenv("TD_API_HASH"));
// TODO port OptionsParser to Windows
// TODO use OptionParser
for (int i = 1; i < argc; i++) {
if (!std::strcmp(argv[i], "--test")) {
use_test_dc = true;
Expand Down
17 changes: 3 additions & 14 deletions tdutils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR "lib")
endif()

if (WIN32)
if (WINGETOPT_FOUND)
set(TD_HAVE_GETOPT 1)
endif()
else()
set(TD_HAVE_GETOPT 1)
endif()

if (NOT ZLIB_FOUND)
find_package(ZLIB)
endif()
Expand Down Expand Up @@ -104,7 +96,7 @@ set(TDUTILS_SOURCE
td/utils/misc.cpp
td/utils/MimeType.cpp
td/utils/MpmcQueue.cpp
td/utils/OptionsParser.cpp
td/utils/OptionParser.cpp
td/utils/PathView.cpp
td/utils/Random.cpp
td/utils/SharedSlice.cpp
Expand Down Expand Up @@ -222,7 +214,7 @@ set(TDUTILS_SOURCE
td/utils/ObjectPool.h
td/utils/Observer.h
td/utils/optional.h
td/utils/OptionsParser.h
td/utils/OptionParser.h
td/utils/OrderedEventsProcessor.h
td/utils/overloaded.h
td/utils/Parser.h
Expand Down Expand Up @@ -280,6 +272,7 @@ set(TDUTILS_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcQueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcWaiter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/MpscLinkQueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OptionParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OrderedEventsProcessor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/port.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp
Expand Down Expand Up @@ -324,10 +317,6 @@ if (ABSL_FOUND)
target_link_libraries(tdutils PUBLIC absl::flat_hash_map absl::flat_hash_set absl::hash)
endif()

if (WIN32 AND WINGETOPT_FOUND)
target_link_libraries(tdutils PRIVATE wingetopt)
endif()

if (ANDROID)
target_link_libraries(tdutils PRIVATE log)
endif()
Expand Down
153 changes: 153 additions & 0 deletions tdutils/td/utils/OptionParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//
// Copyright Aliaksei Levin ([email protected]), Arseny Smirnov ([email protected]) 2014-2020
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/OptionParser.h"

#include "td/utils/misc.h"

#include <cstring>
#include <unordered_map>

namespace td {

void OptionParser::set_description(string description) {
description_ = std::move(description);
}

void OptionParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
std::function<Status(Slice)> callback) {
options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
}

void OptionParser::add_option(char short_key, Slice long_key, Slice description,
std::function<Status(Slice)> callback) {
add_option(Option::Type::Arg, short_key, long_key, description, std::move(callback));
}

void OptionParser::add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback) {
// Ouch. There must be some better way
add_option(Option::Type::NoArg, short_key, long_key, description,
std::bind([](std::function<Status(void)> &func, Slice) { return func(); }, std::move(callback),
std::placeholders::_1));
}

Result<vector<char *>> OptionParser::run(int argc, char *argv[]) {
std::unordered_map<char, const Option *> short_options;
std::unordered_map<string, const Option *> long_options;
for (auto &opt : options_) {
if (opt.short_key != '\0') {
short_options[opt.short_key] = &opt;
}
if (!opt.long_key.empty()) {
long_options[opt.long_key] = &opt;
}
}

vector<char *> non_options;
for (int arg_pos = 1; arg_pos < argc; arg_pos++) {
const char *arg = argv[arg_pos];
if (arg[0] != '-' || arg[1] == '\0') {
non_options.push_back(argv[arg_pos]);
continue;
}
if (arg[1] == '-' && arg[2] == '\0') {
// "--"; after it everything is non-option
while (++arg_pos < argc) {
non_options.push_back(argv[arg_pos]);
}
break;
}

if (arg[1] == '-') {
// long option
Slice long_arg(arg + 2, std::strlen(arg + 2));
Slice param;
auto equal_pos = long_arg.find('=');
bool has_equal = equal_pos != Slice::npos;
if (has_equal) {
param = long_arg.substr(equal_pos + 1);
long_arg = long_arg.substr(0, equal_pos);
}

auto it = long_options.find(long_arg.str());
if (it == long_options.end()) {
return Status::Error(PSLICE() << "Option " << long_arg << " was unrecognized");
}

auto option = it->second;
switch (option->type) {
case Option::Type::NoArg:
if (has_equal) {
return Status::Error(PSLICE() << "Option " << long_arg << " must not have argument");
}
break;
case Option::Type::Arg:
if (!has_equal) {
if (++arg_pos == argc) {
return Status::Error(PSLICE() << "Option " << long_arg << " must have argument");
}
param = Slice(argv[arg_pos], std::strlen(argv[arg_pos]));
}
break;
default:
UNREACHABLE();
}

TRY_STATUS(option->arg_callback(param));
continue;
}

for (size_t opt_pos = 1; arg[opt_pos] != '\0'; opt_pos++) {
auto it = short_options.find(arg[opt_pos]);
if (it == short_options.end()) {
return Status::Error(PSLICE() << "Option " << arg[opt_pos] << " was unrecognized");
}

auto option = it->second;
Slice param;
switch (option->type) {
case Option::Type::NoArg:
// nothing to do
break;
case Option::Type::Arg:
if (arg[opt_pos + 1] == '\0') {
if (++arg_pos == argc) {
return Status::Error(PSLICE() << "Option " << arg[opt_pos] << " must have argument");
}
param = Slice(argv[arg_pos], std::strlen(argv[arg_pos]));
} else {
param = Slice(arg + opt_pos + 1, std::strlen(arg + opt_pos + 1));
opt_pos += param.size();
}
break;
default:
UNREACHABLE();
}

TRY_STATUS(option->arg_callback(param));
}
}

return std::move(non_options);
}

StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o) {
sb << o.description_ << "\n";
for (auto &opt : o.options_) {
sb << "-" << opt.short_key;
if (!opt.long_key.empty()) {
sb << "|--" << opt.long_key;
}
if (opt.type != OptionParser::Option::Type::NoArg) {
sb << "<arg>";
}
sb << "\t" << opt.description;
sb << "\n";
}
return sb;
}

} // namespace td
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace td {

class OptionsParser {
class OptionParser {
class Option {
public:
enum class Type { NoArg, Arg };
Expand All @@ -36,9 +36,10 @@ class OptionsParser {

void add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback);

Result<int> run(int argc, char *argv[]) TD_WARN_UNUSED_RESULT;
// returns found non-option parameters
Result<vector<char *>> run(int argc, char *argv[]) TD_WARN_UNUSED_RESULT;

friend StringBuilder &operator<<(StringBuilder &sb, const OptionsParser &o);
friend StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o);

private:
vector<Option> options_;
Expand Down
123 changes: 0 additions & 123 deletions tdutils/td/utils/OptionsParser.cpp

This file was deleted.

1 change: 0 additions & 1 deletion tdutils/td/utils/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@
#cmakedefine01 TD_HAVE_CRC32C
#cmakedefine01 TD_HAVE_COROUTINES
#cmakedefine01 TD_HAVE_ABSL
#cmakedefine01 TD_HAVE_GETOPT
#cmakedefine01 TD_FD_DEBUG
Loading

0 comments on commit c46910d

Please sign in to comment.