summaryrefslogtreecommitdiff
path: root/src/cam
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-01-22 05:03:54 +0200
committerNiklas Söderlund <niklas.soderlund@ragnatech.se>2019-01-22 14:04:09 +0100
commitf5e48ebf447ec15ba653fa3524e9b9de8e5eb736 (patch)
tree8b0b926612198fffa218e77a6d557f539aafc44f /src/cam
parentfba3d0460750fb4123d66f55315dbd9b34cc3d90 (diff)
cam: Extract option parser to separate file
And turn it into an OptionsParser object. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Diffstat (limited to 'src/cam')
-rw-r--r--src/cam/cam.cpp144
-rw-r--r--src/cam/main.cpp85
-rw-r--r--src/cam/meson.build3
-rw-r--r--src/cam/options.cpp192
-rw-r--r--src/cam/options.h62
5 files changed, 341 insertions, 145 deletions
diff --git a/src/cam/cam.cpp b/src/cam/cam.cpp
deleted file mode 100644
index 0f795be7..00000000
--- a/src/cam/cam.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * main.cpp - cam-ctl a tool to interact with the library
- */
-
-#include <getopt.h>
-#include <iomanip>
-#include <iostream>
-#include <map>
-#include <string.h>
-
-#include <libcamera/libcamera.h>
-
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
-
-using namespace std;
-using namespace libcamera;
-
-enum Option {
- OptCamera = 'c',
- OptHelp = 'h',
- OptList = 'l',
- OptLast = 0,
-};
-
-struct OptionInfo {
- Option id;
- const char *name;
- const char *arguments;
- const char *description;
-};
-
-static struct OptionInfo option_info[] = {
- { OptCamera, "camera", "<camera>", "Specify which camera to operate on" },
- { OptHelp, "help", nullptr, "Display this help message" },
- { OptList, "list", nullptr, "List all cameras" },
- { OptLast, nullptr, nullptr, nullptr },
-};
-
-std::map<Option, std::string> options;
-
-void usage()
-{
- struct OptionInfo *info;
-
- cout << "Options:" << endl;
- for (info = option_info; info->id != OptLast; info++) {
- string arg(info->name);
-
- if (info->arguments)
- arg += string(" ") + info->arguments;
-
- cout << " -" << static_cast<char>(info->id) << " --" <<
- setw(20) << left << arg << " - " <<
- info->description << endl;
- }
-}
-
-int parseOptions(int argc, char **argv)
-{
- char short_options[ARRAY_SIZE(option_info) * 2 + 1];
- struct option long_options[ARRAY_SIZE(option_info)];
- struct OptionInfo *info;
- unsigned ids = 0, idl = 0;
-
- memset(short_options, 0, sizeof(short_options));
- memset(long_options, 0, sizeof(long_options));
-
- for (info = option_info; info->id != OptLast; info++) {
- short_options[ids++] = info->id;
- if (info->arguments)
- short_options[ids++] = ':';
-
- long_options[idl].name = info->name;
- long_options[idl].has_arg =
- info->arguments ? required_argument : no_argument;
- long_options[idl].flag = 0;
- long_options[idl].val = info->id;
- idl++;
- }
-
- while (true) {
- int c = getopt_long(argc, argv, short_options, long_options, nullptr);
-
- if (c == -1)
- break;
-
- if (!isalpha(c))
- return EXIT_FAILURE;
-
- options[static_cast<Option>(c)] = optarg ? string(optarg) : "";
- }
-
- return 0;
-}
-
-bool optSet(Option opt)
-{
- return options.count(opt) != 0;
-}
-
-int main(int argc, char **argv)
-{
- int ret;
-
- ret = parseOptions(argc, argv);
- if (ret == EXIT_FAILURE)
- return ret;
-
- if (argc == 1 || optSet(OptHelp)) {
- usage();
- return 0;
- }
-
- CameraManager *cm = CameraManager::instance();
-
- ret = cm->start();
- if (ret) {
- cout << "Failed to start camera manager: " << strerror(-ret) << endl;
- return EXIT_FAILURE;
- }
-
- if (optSet(OptList)) {
- cout << "Available cameras:" << endl;
- for (const std::shared_ptr<Camera> &camera : cm->cameras())
- cout << "- " << camera->name() << endl;
- }
-
- if (optSet(OptCamera)) {
- std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
-
- if (cam) {
- cout << "Using camera " << cam->name() << endl;
- } else {
- cout << "Camera " << options[OptCamera] << " not found" << endl;
- }
- }
-
- cm->stop();
-
- return 0;
-}
diff --git a/src/cam/main.cpp b/src/cam/main.cpp
new file mode 100644
index 00000000..22211670
--- /dev/null
+++ b/src/cam/main.cpp
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * main.cpp - cam - The libcamera swiss army knife
+ */
+
+#include <iostream>
+#include <map>
+#include <string.h>
+
+#include <libcamera/libcamera.h>
+
+#include "options.h"
+
+using namespace libcamera;
+
+OptionsParser::Options options;
+
+enum {
+ OptCamera = 'c',
+ OptHelp = 'h',
+ OptList = 'l',
+};
+
+static int parseOptions(int argc, char *argv[])
+{
+ OptionsParser parser;
+
+ parser.addOption(OptCamera, "Specify which camera to operate on",
+ "camera", OptionsParser::ArgumentRequired,
+ "camera");
+ parser.addOption(OptHelp, "Display this help message", "help");
+ parser.addOption(OptList, "List all cameras", "list");
+
+ options = std::move(parser.parse(argc, argv));
+ if (!options.valid())
+ return -EINVAL;
+
+ if (argc == 1 || options.isSet(OptHelp)) {
+ parser.usage();
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ ret = parseOptions(argc, argv);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ CameraManager *cm = CameraManager::instance();
+
+ ret = cm->start();
+ if (ret) {
+ std::cout << "Failed to start camera manager: "
+ << strerror(-ret) << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (options.isSet(OptList)) {
+ std::cout << "Available cameras:" << std::endl;
+ for (const std::shared_ptr<Camera> &camera : cm->cameras())
+ std::cout << "- " << camera->name() << std::endl;
+ }
+
+ if (options.isSet(OptCamera)) {
+ std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
+
+ if (cam) {
+ std::cout << "Using camera " << cam->name() << std::endl;
+ } else {
+ std::cout << "Camera " << options[OptCamera]
+ << " not found" << std::endl;
+ }
+ }
+
+ cm->stop();
+
+ return 0;
+}
diff --git a/src/cam/meson.build b/src/cam/meson.build
index 809a40e0..e45e5391 100644
--- a/src/cam/meson.build
+++ b/src/cam/meson.build
@@ -1,5 +1,6 @@
cam_sources = files([
- 'cam.cpp',
+ 'main.cpp',
+ 'options.cpp',
])
cam = executable('cam', cam_sources,
diff --git a/src/cam/options.cpp b/src/cam/options.cpp
new file mode 100644
index 00000000..d391a0e5
--- /dev/null
+++ b/src/cam/options.cpp
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * options.cpp - cam - Options parsing
+ */
+
+#include <getopt.h>
+#include <iomanip>
+#include <iostream>
+#include <string.h>
+
+#include "options.h"
+
+void OptionsParser::addOption(int opt, const char *help, const char *name,
+ OptionArgument argument, const char *argumentName)
+{
+ /*
+ * Options must have at least a short or long name, and a text message.
+ * If an argument is accepted, it must be described by argumentName.
+ */
+ if (!isalnum(opt) && !name)
+ return;
+ if (!help || help[0] == '\0')
+ return;
+ if (argument != ArgumentNone && !argumentName)
+ return;
+
+ /* Reject duplicate options. */
+ if (optionsMap_.find(opt) != optionsMap_.end())
+ return;
+
+ options_.push_back(Option({ opt, name, argument, argumentName, help }));
+ optionsMap_[opt] = &options_.back();
+}
+
+OptionsParser::Options OptionsParser::parse(int argc, char **argv)
+{
+ OptionsParser::Options options;
+
+ /*
+ * Allocate short and long options arrays large enough to contain all
+ * options.
+ */
+ char shortOptions[options_.size() * 3 + 2] = {};
+ struct option longOptions[options_.size() + 1] = {};
+ unsigned int ids = 0;
+ unsigned int idl = 0;
+
+ shortOptions[ids++] = ':';
+
+ for (const Option &option : options_) {
+ if (option.hasShortOption()) {
+ shortOptions[ids++] = option.opt;
+ if (option.argument != ArgumentNone)
+ shortOptions[ids++] = ':';
+ if (option.argument == ArgumentOptional)
+ shortOptions[ids++] = ':';
+ }
+
+ if (option.hasLongOption()) {
+ longOptions[idl].name = option.name;
+
+ switch (option.argument) {
+ case ArgumentNone:
+ longOptions[idl].has_arg = no_argument;
+ break;
+ case ArgumentRequired:
+ longOptions[idl].has_arg = required_argument;
+ break;
+ case ArgumentOptional:
+ longOptions[idl].has_arg = optional_argument;
+ break;
+ }
+
+ longOptions[idl].flag = 0;
+ longOptions[idl].val = option.opt;
+ idl++;
+ }
+ }
+
+ opterr = 0;
+
+ while (true) {
+ int c = getopt_long(argc, argv, shortOptions, longOptions, nullptr);
+
+ if (c == -1)
+ break;
+
+ if (c == '?' || c == ':') {
+ if (c == '?')
+ std::cerr << "Invalid option ";
+ else
+ std::cerr << "Missing argument for option ";
+ std::cerr << argv[optind - 1] << std::endl;
+
+ usage();
+ options.clear();
+ break;
+ }
+
+ options.values_[c] = optarg ? optarg : "";
+ }
+
+ return std::move(options);
+}
+
+void OptionsParser::usage()
+{
+ std::cerr << "Options:" << std::endl;
+
+ unsigned int indent = 0;
+
+ for (const Option &option : options_) {
+ unsigned int length = 14;
+ if (option.hasLongOption())
+ length += 2 + strlen(option.name);
+ if (option.argument != ArgumentNone)
+ length += 1 + strlen(option.argumentName);
+ if (option.argument == ArgumentOptional)
+ length += 2;
+
+ if (length > indent)
+ indent = length;
+ }
+
+ indent = (indent + 7) / 8 * 8;
+
+ for (const Option &option : options_) {
+ std::string argument;
+ if (option.hasShortOption())
+ argument = std::string(" -")
+ + static_cast<char>(option.opt);
+ else
+ argument = " ";
+
+ if (option.hasLongOption()) {
+ if (option.hasShortOption())
+ argument += ", ";
+ else
+ argument += " ";
+ argument += std::string("--") + option.name;
+ };
+
+ if (option.argument != ArgumentNone) {
+ argument += std::string(" ");
+ if (option.argument == ArgumentOptional)
+ argument += "[";
+ argument += option.argumentName;
+ if (option.argument == ArgumentOptional)
+ argument += "]";
+ }
+
+ std::cerr << std::setw(indent) << std::left << argument;
+ std::cerr << option.help << std::endl;
+ }
+}
+
+OptionsParser::Options::Options()
+{
+}
+
+OptionsParser::Options::Options(Options &&other)
+ : values_(std::move(other.values_))
+{
+}
+
+OptionsParser::Options &OptionsParser::Options::operator=(Options &&other)
+{
+ values_ = other.values_;
+ return *this;
+}
+
+bool OptionsParser::Options::valid() const
+{
+ return !values_.empty();
+}
+
+bool OptionsParser::Options::isSet(int opt) const
+{
+ return values_.find(opt) != values_.end();
+}
+
+const std::string &OptionsParser::Options::operator[](int opt) const
+{
+ return values_.find(opt)->second;
+}
+
+void OptionsParser::Options::clear()
+{
+ values_.clear();
+}
diff --git a/src/cam/options.h b/src/cam/options.h
new file mode 100644
index 00000000..88336dfe
--- /dev/null
+++ b/src/cam/options.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * options.h - cam - Options parsing
+ */
+#ifndef __CAM_OPTIONS_H__
+#define __CAM_OPTIONS_H__
+
+#include <ctype.h>
+#include <map>
+#include <vector>
+
+class OptionsParser
+{
+public:
+ enum OptionArgument {
+ ArgumentNone,
+ ArgumentRequired,
+ ArgumentOptional,
+ };
+
+ class Options {
+ public:
+ Options();
+ Options(Options &&other);
+ Options &operator=(Options &&other);
+
+ bool valid() const;
+ bool isSet(int opt) const;
+ const std::string &operator[](int opt) const;
+
+ private:
+ friend class OptionsParser;
+ std::map<int, std::string> values_;
+ void clear();
+ };
+
+ void addOption(int opt, const char *help, const char *name = nullptr,
+ OptionArgument argument = ArgumentNone,
+ const char *argumentName = nullptr);
+
+ Options parse(int argc, char *argv[]);
+ void usage();
+
+private:
+ struct Option {
+ int opt;
+ const char *name;
+ OptionArgument argument;
+ const char *argumentName;
+ const char *help;
+
+ bool hasShortOption() const { return isalnum(opt); }
+ bool hasLongOption() const { return name != nullptr; }
+ };
+
+ std::vector<Option> options_;
+ std::map<unsigned int, Option *> optionsMap_;
+};
+
+#endif /* __CAM_OPTIONS_H__ */