c++ – Simple command line parser

So here’s a simple command line parser. Is it safe? I can’t use anything but C++14 and standard library.

class cli {
public:
  struct parameter {
    struct help {};
    struct single {};

    template <typename T = void>
    struct value {
      using type = T;
    };

    parameter(const std::string& key)
      : key_{key}
      , description_{""}
      , type_info_(typeid(void)) {
    }

    parameter(const std::string& key, help, const std::string& description = "")
      : key_{key}
      , description_{description}
      , type_info_(typeid(help)) {
    }

    parameter(const std::string& key, single, const std::string& description = "")
      : key_{key}
      , description_{description}
      , type_info_(typeid(single)) {
    }

    template <typename T>
    parameter(const std::string& key, value<T>, const std::string& description, T argument)
      : key_{key}
      , description_{description}
      , type_info_(typeid(T)) {
      std::stringstream ss;
      ss << argument;
      argument_ = ss.str();
    }

    static std::string trim_dashs(const std::string& arg) {
      if (arg.size() > 2 && arg(0) == '-' && arg(1) == '-')
        return {arg.begin() + 2, arg.end()};
      if (arg.size() > 1 && arg(0) == '-')
        return {arg.begin() + 1, arg.end()};
      return arg;
    }

    static bool is_valid_key(const std::string& in) {
      return (in.size() == 2 && in(0) == '-' && in(2) >= 'A' && in(2) <= 'z') ||
             (in.size() >= 3 && in(0) == '-' && in(1) == '-' && in(2) >= 'A' && in(2) <= 'z');
    }

    std::string key() const {
      return key_;
    }

    std::string description() const {
      return description_;
    }

    template <typename T>
    T as() const {
      assert((typeid(T).hash_code() == type_info_.get().hash_code()) ||
             ((typeid(T).hash_code() == typeid(bool).hash_code()) &&
              (typeid(parameter::single).hash_code() == type_info_.get().hash_code())));
      T arg;
      std::stringstream ss(argument_);
      ss >> arg;
      return arg;
    }

    template <typename T = void>
    bool is() const {
      return typeid(T).hash_code() == type_info_.get().hash_code() ||
             typeid(T).hash_code() == typeid(parameter::value<void>).hash_code();
    }

    void argument(const std::string& arg) {
      assert(typeid(parameter::help).hash_code() != type_info_.get().hash_code() &&
             typeid(parameter::single).hash_code() != type_info_.get().hash_code());
      argument_ = arg;
    }

    void argument(bool value) {
      argument_ = value ? "true" : "false";
    }

    bool operator <(const parameter& r) const {
      return key() < r.key();
    }

  private:
    std::string key_;
    std::string description_;
    std::string argument_;
    std::reference_wrapper<const std::type_info> type_info_;
  };

public:
  cli(const std::set<parameter> parameters, int argc, char** argv) 
    : command_line_{parameters} {
    std::vector<std::string> argvector;
    for (int i = 1; i < argc; ++i) {
      argvector.push_back(argv(i));
    }
    auto is_defined = (this)(const std::string& in) {
      return command_line_.find({in, parameter::single()}) != command_line_.end();
    };
    for (auto it = argvector.begin(); it != argvector.end(); ++it) {
      auto key = parameter::trim_dashs(*it);
      if (!parameter::is_valid_key(*it) || !is_defined(key)) {
        throw std::runtime_error(std::string("Unknown parameter '") + key + std::string("'n") + help_message());
      }

      parameter p = *command_line_.find({key});

      if (p.is<parameter::help>()) {
        std::cerr << help_message() << std::endl;
        exit(0);
      }

      auto next_it = std::next(it);
      const bool is_valid_argument = next_it != argvector.end() && !parameter::is_valid_key(*next_it);
      if (p.is<parameter::value<>>()) {
        if (is_valid_argument) {
          p.argument(*next_it);
          std::advance(it, 1);
        } else {
          throw std::runtime_error(std::string("Missing argument for '") + key + std::string("'n"));
        }
      } else {
        p.argument("true");
      }

      std::pair<decltype(parameters_)::iterator, bool> inserted = parameters_.insert(p);
      if (not inserted.second)
        throw std::runtime_error(std::string("Double parameter: '") + key + std::string("'n"));
    }
  }

  const parameter& operator() (const std::string& key) {
    if (parameters_.count({key}))
      return *parameters_.find({key});
    auto p = command_line_.find({key});
    return *p;
  }

  std::string help_message() const {
    std::string message = "Usage: builder (options)n"
                          "Allowed options:n";
    for (auto& argument : command_line_) {
      message += std::string("t--");
      message += argument.key();
      message += std::string(std::max(0, 10 - static_cast<int>(argument.key().size())), ' ');
      message += std::string(":");
      message += argument.description();
      message += std::string("n");
    }

    return message;
  }

private:
  std::set<parameter> command_line_;
  std::set<parameter> parameters_;
};

And in the target code:

const std::set<cli::parameter> parameters = {
    {"help", cli::parameter::help(), "help message"},
    {"config", cli::parameter::value<std::string>(), "default configuration", std::string("Debug")},
    {"install", cli::parameter::single(), "build install target"},
    {"pack", cli::parameter::single(), "build package target"},
    {"timeout", cli::parameter::value<uint16_t>(), "timeout in seconds", uint16_t(10)}
  };

cli c(parameters, argc, argv);
foo bar(cli("timeout").as<uint16_t>());

command line – how to find the jobs run by each local users

I am interested in finding what applications/scripts are executed by each linux user.

I can find the number of jobs run by a specific user with the following command

ps -u <USERNAME> -U <USERNAME>

However it shows the jobs of a specific provided user whereas I want it for all users or all local users.

E.g. the following command count the number of jobs by each user

ps -eo user=|sort|uniq -c

I want a similar command but i want to see all the processes run by a user. Thanks.

command line – cl date function working differently in Intel and M1 machines

I have a shell script to calculate the number of days from a particular date:

echo D$((($(date +%s)-$(date +%s --date "2018-01-01"))/(3600*24)))

On my 2019 MBP (Intel), it works. On my 2020 Mac mini (M1), it doesn’t.

The error:

date: illegal time format
usage: date (-jnRu) (-d dst) (-r seconds) (-t west) (-v(+|-)val(ymwdHMS)) ...
            (-f fmt date | (((mm)dd)HH)MM((cc)yy)(.ss)) (+format)
/usr/local/bin/days: line 1: (1614815225-)/(3600*24): syntax error: operand expected (error token is ")/(3600*24)")

The man pages are different. On the MBP it’s

DATE(1) User Commands DATE(1)

On the Mac mini it’s

DATE(1)  BSD General Commands Manual  DATE(1)
  1. What’s going on here?
  2. How do I get back the old functionality?

How to install Homebrew without Command Line Tools if already have Xcode

Apple’s website for downloading developer tools says that “If you use Xcode, these tools are already embedded in the IDE.”

So you shouldn’t need to install them again.

But, as mentioned in the commments: it’s a 460 Mb download. If you don’t have space for that, you’re not going to have space for HomeBrew, or any of the things it installs.

calculus and analysis – An alternative command to compute a logarithmic integral

I am trying to see if Mathematica can calculate:
$$int_0^1frac{ln(x)ln(1-x)ln(1+x)}{x}dx,$$
which has a well-known closed form. So I tried
Integrate(Log(x)Log(1-x)Log(1+x)/x,{x,0,1}) but I waited for about 20 mins and it didn’t show any result, so I stopped the calculations. Is there a command that helps Mathematica calculate the meant integral within few mins?

syntax – What does the option ‘Complexexpand’ do in the Collect command?

I’m trying to re-write a long chain of commands I’ve received in pdf from Mathematica language to Maxima. I have this command:

Collect(%, ε, ComplexExpand)

I know what the Collect command does. But what does the ComplexExpand option do here? Will it perform

Collect(ComplexExpand(%),ε)

or

ComplexExpand(Collect(%),ε)

or something else? I did not see the ComplexExpand option for Collect in the documentation. Thanks in advance.

command line interface – How to script out an install with flags?

I’ve downloaded an installer file in .msi format. I’m wanting to figure a way out to script the installer so that I can run it through one our automation tools and have it install and configure the software on hundreds of servers in our environment.

I looked at tools such as Silent Install Builder, and found it very easy to use. Unfortunately, our Enterprise InfoSec tools flag the bundle as malware, so I figured I would just do it via command line since the original .msi itself installs with no issues.

For the install, I’m wanting it to choose “Typical” for the setup type.

On the next screen, I’m wanting to choose both radio buttons.

And finally, I’m wanting it to input an IP address in the “Allowed hosts” field, and choose every module

I’ve looked around online at how to do this via command line, but unfortunately I was unsuccessful with having it choose the setup type, tick both radio buttons, and input the IP address for me and choose every module listed.

ipad – Copy files from iOS to Arduino using dd command?

I’m having an Arduino based MCU which is capable of running compiled .bin files by just copying them onto the device (connected via USB) using the file explorer. After the file was copied (e.g. with drag and drop) the MCU detects the new .bin file and restarts. That’s working flawlessly on Windows and Linux.

On macOS however, drag and drop is not working. After copying the file using Finder, the MCU does not detect changes and doesn’t restart. Here, I either have to use muCommander or the dd command.

I just wanted to try that on iOS by connecting the MCU to the USB-C / Lightning port of different iPads. The MCU is detected by the Files App, but copying a compiled .bin file to the MCU with the Files app results in the same behaviour as on macOS. I guess both platforms are using similar copying methods.

Is there any way of copying files on iOS using the dd command? Is there any file browser like muCommander to copy files on iOS?

putty – Bitvise SSH port forwarding dynamic command

I need functionality “port forwarding dynamic” to be able to fake ip with ssh.
The statement is similar to putty

putty -l user -pw pass host -D 1247

I found the bitvise command instructions at https://www.bitvise.com/files/guides/BvSshClient-Params.txt.The command I have executed

stnlc user@host -pw=pass -proxy=y -proxyType=SOCKS5 -proxyServer=127.0.0.1 -proxyPort=1247

But it doesn’t work please help me

command line – Run a piece of code in the background while using a script

Hey there I have a (for me atleast) complicated question.

I have a script that interacts with the plex (and sonarr but that isn’t important) api. I made it look and work like an interface. See the pictures:

I run the script and this is the first thing I see:

Home page

I choose to interact with the plex api and get to see the following page:

Home page plex api

There appears to be an update available for pms so I select “update”:

Updating but someone is using plex

But someone is watching a movie or series. So it doesn’t update because updating shuts down pms which would stop the stream of that person. So I made the script show that message when that happens (instead of updating). It shows it for 10 seconds and after that you go back to the menu in picture 2.

When nobody is streaming, it just goes through the update process and after that brings you back to Home page Plex API.

Now comes the question

I want to make an option called notify (an option like I could choose “plex” and “update” and now I want “notify”). So at the bottom of the message in picture 3, I would have exit | back | notify: (instead of sleeping 10s and going to Home page Plex API).

When I choose that option, it needs to run some code in the background. So it immediately goes back to Home page Plex API but in the background, it checks every 2 minutes if there’s still anybody watching. And when there isn’t anymore, it sets a variable to false.

But the problem for me is “in the background”. I want to run that piece of code in the background so that I could use the script in the mean time. So I choose “notify” and am then immediately able to use the script again. I can do (aka choose options/access parts of the api) anything I want, while in the background that part of code is running. And after it detects that that variable is set to false, it stops running in the background.

Do you have any idea how to do this?

Normally I’m already trying things and testing out if something works but with this problem, I really don’t know what to do/where to begin.