diff options
Diffstat (limited to 'src/cam')
-rw-r--r-- | src/cam/buffer_writer.cpp | 88 | ||||
-rw-r--r-- | src/cam/buffer_writer.h | 31 | ||||
-rw-r--r-- | src/cam/capture.cpp | 204 | ||||
-rw-r--r-- | src/cam/capture.h | 44 | ||||
-rw-r--r-- | src/cam/event_loop.cpp | 39 | ||||
-rw-r--r-- | src/cam/event_loop.h | 34 | ||||
-rw-r--r-- | src/cam/main.cpp | 385 | ||||
-rw-r--r-- | src/cam/main.h | 21 | ||||
-rw-r--r-- | src/cam/meson.build | 11 | ||||
-rw-r--r-- | src/cam/options.cpp | 537 | ||||
-rw-r--r-- | src/cam/options.h | 147 |
11 files changed, 0 insertions, 1541 deletions
diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp deleted file mode 100644 index c5a5eb46..00000000 --- a/src/cam/buffer_writer.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * buffer_writer.cpp - Buffer writer - */ - -#include <fcntl.h> -#include <iomanip> -#include <iostream> -#include <sstream> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> - -#include "buffer_writer.h" - -using namespace libcamera; - -BufferWriter::BufferWriter(const std::string &pattern) - : pattern_(pattern) -{ -} - -BufferWriter::~BufferWriter() -{ - for (auto &iter : mappedBuffers_) { - void *memory = iter.second.first; - unsigned int length = iter.second.second; - munmap(memory, length); - } - mappedBuffers_.clear(); -} - -void BufferWriter::mapBuffer(FrameBuffer *buffer) -{ - for (const FrameBuffer::Plane &plane : buffer->planes()) { - void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, - plane.fd.fd(), 0); - - mappedBuffers_[plane.fd.fd()] = - std::make_pair(memory, plane.length); - } -} - -int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName) -{ - std::string filename; - size_t pos; - int fd, ret = 0; - - filename = pattern_; - pos = filename.find_first_of('#'); - if (pos != std::string::npos) { - std::stringstream ss; - ss << streamName << "-" << std::setw(6) - << std::setfill('0') << buffer->metadata().sequence; - filename.replace(pos, 1, ss.str()); - } - - fd = open(filename.c_str(), O_CREAT | O_WRONLY | - (pos == std::string::npos ? O_APPEND : O_TRUNC), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - if (fd == -1) - return -errno; - - for (const FrameBuffer::Plane &plane : buffer->planes()) { - void *data = mappedBuffers_[plane.fd.fd()].first; - unsigned int length = plane.length; - - ret = ::write(fd, data, length); - if (ret < 0) { - ret = -errno; - std::cerr << "write error: " << strerror(-ret) - << std::endl; - break; - } else if (ret != (int)length) { - std::cerr << "write error: only " << ret - << " bytes written instead of " - << length << std::endl; - break; - } - } - - close(fd); - - return ret; -} diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h deleted file mode 100644 index 8c9b2436..00000000 --- a/src/cam/buffer_writer.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * buffer_writer.h - Buffer writer - */ -#ifndef __LIBCAMERA_BUFFER_WRITER_H__ -#define __LIBCAMERA_BUFFER_WRITER_H__ - -#include <map> -#include <string> - -#include <libcamera/buffer.h> - -class BufferWriter -{ -public: - BufferWriter(const std::string &pattern = "frame-#.bin"); - ~BufferWriter(); - - void mapBuffer(libcamera::FrameBuffer *buffer); - - int write(libcamera::FrameBuffer *buffer, - const std::string &streamName); - -private: - std::string pattern_; - std::map<int, std::pair<void *, unsigned int>> mappedBuffers_; -}; - -#endif /* __LIBCAMERA_BUFFER_WRITER_H__ */ diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp deleted file mode 100644 index 55fa2dab..00000000 --- a/src/cam/capture.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * capture.cpp - Cam capture - */ - -#include <chrono> -#include <iomanip> -#include <iostream> -#include <limits.h> -#include <sstream> - -#include "capture.h" -#include "main.h" - -using namespace libcamera; - -Capture::Capture(std::shared_ptr<Camera> camera, CameraConfiguration *config) - : camera_(camera), config_(config), writer_(nullptr) -{ -} - -int Capture::run(EventLoop *loop, const OptionsParser::Options &options) -{ - int ret; - - if (!camera_) { - std::cout << "Can't capture without a camera" << std::endl; - return -ENODEV; - } - - ret = camera_->configure(config_); - if (ret < 0) { - std::cout << "Failed to configure camera" << std::endl; - return ret; - } - - streamName_.clear(); - for (unsigned int index = 0; index < config_->size(); ++index) { - StreamConfiguration &cfg = config_->at(index); - streamName_[cfg.stream()] = "stream" + std::to_string(index); - } - - camera_->requestCompleted.connect(this, &Capture::requestComplete); - - if (options.isSet(OptFile)) { - if (!options[OptFile].toString().empty()) - writer_ = new BufferWriter(options[OptFile]); - else - writer_ = new BufferWriter(); - } - - - FrameBufferAllocator *allocator = new FrameBufferAllocator(camera_); - - ret = capture(loop, allocator); - - if (options.isSet(OptFile)) { - delete writer_; - writer_ = nullptr; - } - - delete allocator; - - return ret; -} - -int Capture::capture(EventLoop *loop, FrameBufferAllocator *allocator) -{ - int ret; - - /* Identify the stream with the least number of buffers. */ - unsigned int nbuffers = UINT_MAX; - for (StreamConfiguration &cfg : *config_) { - ret = allocator->allocate(cfg.stream()); - if (ret < 0) { - std::cerr << "Can't allocate buffers" << std::endl; - return -ENOMEM; - } - - unsigned int allocated = allocator->buffers(cfg.stream()).size(); - nbuffers = std::min(nbuffers, allocated); - } - - /* - * TODO: make cam tool smarter to support still capture by for - * example pushing a button. For now run all streams all the time. - */ - - std::vector<Request *> requests; - for (unsigned int i = 0; i < nbuffers; i++) { - Request *request = camera_->createRequest(); - if (!request) { - std::cerr << "Can't create request" << std::endl; - return -ENOMEM; - } - - for (StreamConfiguration &cfg : *config_) { - Stream *stream = cfg.stream(); - const std::vector<std::unique_ptr<FrameBuffer>> &buffers = - allocator->buffers(stream); - const std::unique_ptr<FrameBuffer> &buffer = buffers[i]; - - ret = request->addBuffer(stream, buffer.get()); - if (ret < 0) { - std::cerr << "Can't set buffer for request" - << std::endl; - return ret; - } - - if (writer_) - writer_->mapBuffer(buffer.get()); - } - - requests.push_back(request); - } - - ret = camera_->start(); - if (ret) { - std::cout << "Failed to start capture" << std::endl; - return ret; - } - - for (Request *request : requests) { - ret = camera_->queueRequest(request); - if (ret < 0) { - std::cerr << "Can't queue request" << std::endl; - camera_->stop(); - return ret; - } - } - - std::cout << "Capture until user interrupts by SIGINT" << std::endl; - ret = loop->exec(); - if (ret) - std::cout << "Failed to run capture loop" << std::endl; - - ret = camera_->stop(); - if (ret) - std::cout << "Failed to stop capture" << std::endl; - - return ret; -} - -void Capture::requestComplete(Request *request) -{ - if (request->status() == Request::RequestCancelled) - return; - - const std::map<Stream *, FrameBuffer *> &buffers = request->buffers(); - - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - double fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count(); - fps = last_ != std::chrono::steady_clock::time_point() && fps - ? 1000.0 / fps : 0.0; - last_ = now; - - std::stringstream info; - info << "fps: " << std::fixed << std::setprecision(2) << fps; - - for (auto it = buffers.begin(); it != buffers.end(); ++it) { - Stream *stream = it->first; - FrameBuffer *buffer = it->second; - const std::string &name = streamName_[stream]; - - const FrameMetadata &metadata = buffer->metadata(); - - info << " " << name - << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence - << " bytesused: "; - - unsigned int nplane = 0; - for (const FrameMetadata::Plane &plane : metadata.planes) { - info << plane.bytesused; - if (++nplane < metadata.planes.size()) - info << "/"; - } - - if (writer_) - writer_->write(buffer, name); - } - - std::cout << info.str() << std::endl; - - /* - * Create a new request and populate it with one buffer for each - * stream. - */ - request = camera_->createRequest(); - if (!request) { - std::cerr << "Can't create request" << std::endl; - return; - } - - for (auto it = buffers.begin(); it != buffers.end(); ++it) { - Stream *stream = it->first; - FrameBuffer *buffer = it->second; - - request->addBuffer(stream, buffer); - } - - camera_->queueRequest(request); -} diff --git a/src/cam/capture.h b/src/cam/capture.h deleted file mode 100644 index 9bca5661..00000000 --- a/src/cam/capture.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * capture.h - Cam capture - */ -#ifndef __CAM_CAPTURE_H__ -#define __CAM_CAPTURE_H__ - -#include <chrono> -#include <memory> - -#include <libcamera/buffer.h> -#include <libcamera/camera.h> -#include <libcamera/framebuffer_allocator.h> -#include <libcamera/request.h> -#include <libcamera/stream.h> - -#include "buffer_writer.h" -#include "event_loop.h" -#include "options.h" - -class Capture -{ -public: - Capture(std::shared_ptr<libcamera::Camera> camera, - libcamera::CameraConfiguration *config); - - int run(EventLoop *loop, const OptionsParser::Options &options); -private: - int capture(EventLoop *loop, - libcamera::FrameBufferAllocator *allocator); - - void requestComplete(libcamera::Request *request); - - std::shared_ptr<libcamera::Camera> camera_; - libcamera::CameraConfiguration *config_; - - std::map<libcamera::Stream *, std::string> streamName_; - BufferWriter *writer_; - std::chrono::steady_clock::time_point last_; -}; - -#endif /* __CAM_CAPTURE_H__ */ diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp deleted file mode 100644 index e8ab8617..00000000 --- a/src/cam/event_loop.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * event_loop.cpp - cam - Event loop - */ - -#include <libcamera/event_dispatcher.h> - -#include "event_loop.h" - -using namespace libcamera; - -EventLoop::EventLoop(EventDispatcher *dispatcher) - : dispatcher_(dispatcher) -{ -} - -EventLoop::~EventLoop() -{ -} - -int EventLoop::exec() -{ - exitCode_ = -1; - exit_.store(false, std::memory_order_release); - - while (!exit_.load(std::memory_order_acquire)) - dispatcher_->processEvents(); - - return exitCode_; -} - -void EventLoop::exit(int code) -{ - exitCode_ = code; - exit_.store(true, std::memory_order_release); - dispatcher_->interrupt(); -} diff --git a/src/cam/event_loop.h b/src/cam/event_loop.h deleted file mode 100644 index 581c7cba..00000000 --- a/src/cam/event_loop.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * event_loop.h - cam - Event loop - */ -#ifndef __CAM_EVENT_LOOP_H__ -#define __CAM_EVENT_LOOP_H__ - -#include <atomic> - -#include <libcamera/event_notifier.h> - -namespace libcamera { -class EventDispatcher; -} - -class EventLoop -{ -public: - EventLoop(libcamera::EventDispatcher *dispatcher); - ~EventLoop(); - - int exec(); - void exit(int code = 0); - -private: - libcamera::EventDispatcher *dispatcher_; - - std::atomic<bool> exit_; - int exitCode_; -}; - -#endif /* __CAM_EVENT_LOOP_H__ */ diff --git a/src/cam/main.cpp b/src/cam/main.cpp deleted file mode 100644 index 718740f4..00000000 --- a/src/cam/main.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * main.cpp - cam - The libcamera swiss army knife - */ - -#include <iomanip> -#include <iostream> -#include <signal.h> -#include <string.h> - -#include <libcamera/libcamera.h> -#include <libcamera/property_ids.h> - -#include "capture.h" -#include "event_loop.h" -#include "main.h" -#include "options.h" - -using namespace libcamera; - -class CamApp -{ -public: - CamApp(); - ~CamApp(); - - static CamApp *instance(); - - int init(int argc, char **argv); - void cleanup(); - - int exec(); - void quit(); - -private: - int parseOptions(int argc, char *argv[]); - int prepareConfig(); - int listProperties(); - int infoConfiguration(); - int run(); - - static CamApp *app_; - OptionsParser::Options options_; - CameraManager *cm_; - std::shared_ptr<Camera> camera_; - std::unique_ptr<libcamera::CameraConfiguration> config_; - EventLoop *loop_; -}; - -CamApp *CamApp::app_ = nullptr; - -CamApp::CamApp() - : cm_(nullptr), camera_(nullptr), config_(nullptr), loop_(nullptr) -{ - CamApp::app_ = this; -} - -CamApp::~CamApp() -{ - delete cm_; -} - -CamApp *CamApp::instance() -{ - return CamApp::app_; -} - -int CamApp::init(int argc, char **argv) -{ - int ret; - - ret = parseOptions(argc, argv); - if (ret < 0) - return ret; - - cm_ = new CameraManager(); - - ret = cm_->start(); - if (ret) { - std::cout << "Failed to start camera manager: " - << strerror(-ret) << std::endl; - return ret; - } - - if (options_.isSet(OptCamera)) { - const std::string &cameraName = options_[OptCamera]; - char *endptr; - unsigned long index = strtoul(cameraName.c_str(), &endptr, 10); - if (*endptr == '\0' && index > 0 && index <= cm_->cameras().size()) - camera_ = cm_->cameras()[index - 1]; - else - camera_ = cm_->get(cameraName); - - if (!camera_) { - std::cout << "Camera " - << std::string(options_[OptCamera]) - << " not found" << std::endl; - cm_->stop(); - return -ENODEV; - } - - if (camera_->acquire()) { - std::cout << "Failed to acquire camera" << std::endl; - camera_.reset(); - cm_->stop(); - return -EINVAL; - } - - std::cout << "Using camera " << camera_->name() << std::endl; - - ret = prepareConfig(); - if (ret) - return ret; - } - - loop_ = new EventLoop(cm_->eventDispatcher()); - - return 0; -} - -void CamApp::cleanup() -{ - delete loop_; - loop_ = nullptr; - - if (camera_) { - camera_->release(); - camera_.reset(); - } - - config_.reset(); - - cm_->stop(); -} - -int CamApp::exec() -{ - int ret; - - ret = run(); - cleanup(); - - return ret; -} - -void CamApp::quit() -{ - if (loop_) - loop_->exit(); -} - -int CamApp::parseOptions(int argc, char *argv[]) -{ - KeyValueParser streamKeyValue; - streamKeyValue.addOption("role", OptionString, - "Role for the stream (viewfinder, video, still, stillraw)", - ArgumentRequired); - streamKeyValue.addOption("width", OptionInteger, "Width in pixels", - ArgumentRequired); - streamKeyValue.addOption("height", OptionInteger, "Height in pixels", - ArgumentRequired); - streamKeyValue.addOption("pixelformat", OptionInteger, "Pixel format", - ArgumentRequired); - - OptionsParser parser; - parser.addOption(OptCamera, OptionString, - "Specify which camera to operate on, by name or by index", "camera", - ArgumentRequired, "camera"); - parser.addOption(OptCapture, OptionNone, - "Capture until interrupted by user", "capture"); - parser.addOption(OptFile, OptionString, - "Write captured frames to disk\n" - "The first '#' character in the file name is expanded to the stream name and frame sequence number.\n" - "The default file name is 'frame-#.bin'.", - "file", ArgumentOptional, "filename"); - parser.addOption(OptStream, &streamKeyValue, - "Set configuration of a camera stream", "stream", true); - parser.addOption(OptHelp, OptionNone, "Display this help message", - "help"); - parser.addOption(OptInfo, OptionNone, - "Display information about stream(s)", "info"); - parser.addOption(OptList, OptionNone, "List all cameras", "list"); - parser.addOption(OptProps, OptionNone, "List cameras properties", - "list-properties"); - - options_ = parser.parse(argc, argv); - if (!options_.valid()) - return -EINVAL; - - if (options_.empty() || options_.isSet(OptHelp)) { - parser.usage(); - return options_.empty() ? -EINVAL : -EINTR; - } - - return 0; -} - -int CamApp::prepareConfig() -{ - StreamRoles roles; - - if (options_.isSet(OptStream)) { - const std::vector<OptionValue> &streamOptions = - options_[OptStream].toArray(); - - /* Use roles and get a default configuration. */ - for (auto const &value : streamOptions) { - KeyValueParser::Options opt = value.toKeyValues(); - - std::string role = opt.isSet("role") - ? opt["role"].toString() - : "viewfinder"; - - if (role == "viewfinder") { - roles.push_back(StreamRole::Viewfinder); - } else if (role == "video") { - roles.push_back(StreamRole::VideoRecording); - } else if (role == "still") { - roles.push_back(StreamRole::StillCapture); - } else if (role == "stillraw") { - roles.push_back(StreamRole::StillCaptureRaw); - } else { - std::cerr << "Unknown stream role " - << role << std::endl; - return -EINVAL; - } - } - } else { - /* If no configuration is provided assume a single video stream. */ - roles.push_back(StreamRole::VideoRecording); - } - - config_ = camera_->generateConfiguration(roles); - if (!config_ || config_->size() != roles.size()) { - std::cerr << "Failed to get default stream configuration" - << std::endl; - return -EINVAL; - } - - /* Apply configuration if explicitly requested. */ - if (options_.isSet(OptStream)) { - const std::vector<OptionValue> &streamOptions = - options_[OptStream].toArray(); - - unsigned int i = 0; - for (auto const &value : streamOptions) { - KeyValueParser::Options opt = value.toKeyValues(); - StreamConfiguration &cfg = config_->at(i++); - - if (opt.isSet("width")) - cfg.size.width = opt["width"]; - - if (opt.isSet("height")) - cfg.size.height = opt["height"]; - - /* TODO: Translate 4CC string to ID. */ - if (opt.isSet("pixelformat")) - cfg.pixelFormat = PixelFormat(opt["pixelformat"]); - } - } - - switch (config_->validate()) { - case CameraConfiguration::Valid: - break; - case CameraConfiguration::Adjusted: - std::cout << "Camera configuration adjusted" << std::endl; - break; - case CameraConfiguration::Invalid: - std::cout << "Camera configuration invalid" << std::endl; - config_.reset(); - return -EINVAL; - } - - return 0; -} - -int CamApp::listProperties() -{ - if (!camera_) { - std::cout << "Cannot list properties without a camera" - << std::endl; - return -EINVAL; - } - - for (const auto &prop : camera_->properties()) { - const ControlId *id = properties::properties.at(prop.first); - const ControlValue &value = prop.second; - - std::cout << "Property: " << id->name() << " = " - << value.toString() << std::endl; - } - - return 0; -} - -int CamApp::infoConfiguration() -{ - if (!config_) { - std::cout << "Cannot print stream information without a camera" - << std::endl; - return -EINVAL; - } - - unsigned int index = 0; - for (const StreamConfiguration &cfg : *config_) { - std::cout << index << ": " << cfg.toString() << std::endl; - - const StreamFormats &formats = cfg.formats(); - for (PixelFormat pixelformat : formats.pixelformats()) { - std::cout << " * Pixelformat: " - << pixelformat.toString() << " " - << formats.range(pixelformat).toString() - << std::endl; - - for (const Size &size : formats.sizes(pixelformat)) - std::cout << " - " << size.toString() - << std::endl; - } - - index++; - } - - return 0; -} - -int CamApp::run() -{ - int ret; - - if (options_.isSet(OptList)) { - std::cout << "Available cameras:" << std::endl; - - unsigned int index = 1; - for (const std::shared_ptr<Camera> &cam : cm_->cameras()) { - std::cout << index << ": " << cam->name() << std::endl; - index++; - } - } - - if (options_.isSet(OptProps)) { - ret = listProperties(); - if (ret) - return ret; - } - - if (options_.isSet(OptInfo)) { - ret = infoConfiguration(); - if (ret) - return ret; - } - - if (options_.isSet(OptCapture)) { - Capture capture(camera_, config_.get()); - return capture.run(loop_, options_); - } - - return 0; -} - -void signalHandler(int signal) -{ - std::cout << "Exiting" << std::endl; - CamApp::instance()->quit(); -} - -int main(int argc, char **argv) -{ - CamApp app; - int ret; - - ret = app.init(argc, argv); - if (ret) - return ret == -EINTR ? 0 : EXIT_FAILURE; - - struct sigaction sa = {}; - sa.sa_handler = &signalHandler; - sigaction(SIGINT, &sa, nullptr); - - if (app.exec()) - return EXIT_FAILURE; - - return 0; -} diff --git a/src/cam/main.h b/src/cam/main.h deleted file mode 100644 index afcad435..00000000 --- a/src/cam/main.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * main.h - Cam application - */ -#ifndef __CAM_MAIN_H__ -#define __CAM_MAIN_H__ - -enum { - OptCamera = 'c', - OptCapture = 'C', - OptFile = 'F', - OptHelp = 'h', - OptInfo = 'I', - OptList = 'l', - OptProps = 'p', - OptStream = 's', -}; - -#endif /* __CAM_MAIN_H__ */ diff --git a/src/cam/meson.build b/src/cam/meson.build deleted file mode 100644 index 2419d648..00000000 --- a/src/cam/meson.build +++ /dev/null @@ -1,11 +0,0 @@ -cam_sources = files([ - 'buffer_writer.cpp', - 'capture.cpp', - 'event_loop.cpp', - 'main.cpp', - 'options.cpp', -]) - -cam = executable('cam', cam_sources, - dependencies : [ libatomic, libcamera_dep ], - install : true) diff --git a/src/cam/options.cpp b/src/cam/options.cpp deleted file mode 100644 index 2c56eacf..00000000 --- a/src/cam/options.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * options.cpp - cam - Options parsing - */ - -#include <assert.h> -#include <getopt.h> -#include <iomanip> -#include <iostream> -#include <string.h> - -#include "options.h" - -/* ----------------------------------------------------------------------------- - * Option - */ - -const char *Option::typeName() const -{ - switch (type) { - case OptionNone: - return "none"; - - case OptionInteger: - return "integer"; - - case OptionString: - return "string"; - - case OptionKeyValue: - return "key=value"; - } - - return "unknown"; -} - -/* ----------------------------------------------------------------------------- - * OptionBase<T> - */ - -template<typename T> -bool OptionsBase<T>::empty() const -{ - return values_.empty(); -} - -template<typename T> -bool OptionsBase<T>::valid() const -{ - return valid_; -} - -template<typename T> -bool OptionsBase<T>::isSet(const T &opt) const -{ - return values_.find(opt) != values_.end(); -} - -template<typename T> -const OptionValue &OptionsBase<T>::operator[](const T &opt) const -{ - return values_.find(opt)->second; -} - -template<typename T> -bool OptionsBase<T>::parseValue(const T &opt, const Option &option, - const char *optarg) -{ - OptionValue value; - - switch (option.type) { - case OptionNone: - break; - - case OptionInteger: - unsigned int integer; - - if (optarg) { - char *endptr; - integer = strtoul(optarg, &endptr, 0); - if (*endptr != '\0') - return false; - } else { - integer = 0; - } - - value = OptionValue(integer); - break; - - case OptionString: - value = OptionValue(optarg ? optarg : ""); - break; - - case OptionKeyValue: - KeyValueParser *kvParser = option.keyValueParser; - KeyValueParser::Options keyValues = kvParser->parse(optarg); - if (!keyValues.valid()) - return false; - - value = OptionValue(keyValues); - break; - } - - if (option.isArray) - values_[opt].addValue(value); - else - values_[opt] = value; - - return true; -} - -template class OptionsBase<int>; -template class OptionsBase<std::string>; - -/* ----------------------------------------------------------------------------- - * KeyValueParser - */ - -bool KeyValueParser::addOption(const char *name, OptionType type, - const char *help, OptionArgument argument) -{ - if (!name) - return false; - if (!help || help[0] == '\0') - return false; - if (argument != ArgumentNone && type == OptionNone) - return false; - - /* Reject duplicate options. */ - if (optionsMap_.find(name) != optionsMap_.end()) - return false; - - optionsMap_[name] = Option({ 0, type, name, argument, nullptr, - help, nullptr, false }); - return true; -} - -KeyValueParser::Options KeyValueParser::parse(const char *arguments) -{ - Options options; - - for (const char *pair = arguments; *arguments != '\0'; pair = arguments) { - const char *comma = strchrnul(arguments, ','); - size_t len = comma - pair; - - /* Skip over the comma. */ - arguments = *comma == ',' ? comma + 1 : comma; - - /* Skip to the next pair if the pair is empty. */ - if (!len) - continue; - - std::string key; - std::string value; - - const char *separator = static_cast<const char *>(memchr(pair, '=', len)); - if (!separator) { - key = std::string(pair, len); - value = ""; - } else { - key = std::string(pair, separator - pair); - value = std::string(separator + 1, comma - separator - 1); - } - - /* The key is mandatory, the value might be optional. */ - if (key.empty()) - continue; - - if (optionsMap_.find(key) == optionsMap_.end()) { - std::cerr << "Invalid option " << key << std::endl; - return options; - } - - OptionArgument arg = optionsMap_[key].argument; - if (value.empty() && arg == ArgumentRequired) { - std::cerr << "Option " << key << " requires an argument" - << std::endl; - return options; - } else if (!value.empty() && arg == ArgumentNone) { - std::cerr << "Option " << key << " takes no argument" - << std::endl; - return options; - } - - const Option &option = optionsMap_[key]; - if (!options.parseValue(key, option, value.c_str())) { - std::cerr << "Failed to parse '" << value << "' as " - << option.typeName() << " for option " << key - << std::endl; - return options; - } - } - - options.valid_ = true; - return options; -} - -void KeyValueParser::usage(int indent) -{ - unsigned int space = 0; - - for (auto const &iter : optionsMap_) { - const Option &option = iter.second; - unsigned int length = 14; - if (option.argument != ArgumentNone) - length += 1 + strlen(option.typeName()); - if (option.argument == ArgumentOptional) - length += 2; - - if (length > space) - space = length; - } - - space = (space + 7) / 8 * 8; - - for (auto const &iter : optionsMap_) { - const Option &option = iter.second; - std::string argument = option.name; - - if (option.argument != ArgumentNone) { - if (option.argument == ArgumentOptional) - argument += "[="; - else - argument += "="; - argument += option.typeName(); - if (option.argument == ArgumentOptional) - argument += "]"; - } - - std::cerr << std::setw(indent) << std::right << " " - << std::setw(space) << std::left << argument; - - for (const char *help = option.help, *end = help; end;) { - end = strchr(help, '\n'); - if (end) { - std::cerr << std::string(help, end - help + 1); - std::cerr << std::setw(indent + space) << " "; - help = end + 1; - } else { - std::cerr << help << std::endl; - } - } - } -} - -/* ----------------------------------------------------------------------------- - * OptionValue - */ - -OptionValue::OptionValue() - : type_(ValueNone), integer_(0) -{ -} - -OptionValue::OptionValue(int value) - : type_(ValueInteger), integer_(value) -{ -} - -OptionValue::OptionValue(const char *value) - : type_(ValueString), integer_(0), string_(value) -{ -} - -OptionValue::OptionValue(const std::string &value) - : type_(ValueString), integer_(0), string_(value) -{ -} - -OptionValue::OptionValue(const KeyValueParser::Options &value) - : type_(ValueKeyValue), integer_(0), keyValues_(value) -{ -} - -void OptionValue::addValue(const OptionValue &value) -{ - assert(type_ == ValueNone || type_ == ValueArray); - - type_ = ValueArray; - array_.push_back(value); -} - -OptionValue::operator int() const -{ - return toInteger(); -} - -OptionValue::operator std::string() const -{ - return toString(); -} - -OptionValue::operator KeyValueParser::Options() const -{ - return toKeyValues(); -} - -OptionValue::operator std::vector<OptionValue>() const -{ - return toArray(); -} - -int OptionValue::toInteger() const -{ - if (type_ != ValueInteger) - return 0; - - return integer_; -} - -std::string OptionValue::toString() const -{ - if (type_ != ValueString) - return std::string(); - - return string_; -} - -KeyValueParser::Options OptionValue::toKeyValues() const -{ - if (type_ != ValueKeyValue) - return KeyValueParser::Options(); - - return keyValues_; -} - -std::vector<OptionValue> OptionValue::toArray() const -{ - if (type_ != ValueArray) - return std::vector<OptionValue>{}; - - return array_; -} - -/* ----------------------------------------------------------------------------- - * OptionsParser - */ - -bool OptionsParser::addOption(int opt, OptionType type, const char *help, - const char *name, OptionArgument argument, - const char *argumentName, bool array) -{ - /* - * 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 false; - if (!help || help[0] == '\0') - return false; - if (argument != ArgumentNone && !argumentName) - return false; - - /* Reject duplicate options. */ - if (optionsMap_.find(opt) != optionsMap_.end()) - return false; - - options_.push_back(Option({ opt, type, name, argument, argumentName, - help, nullptr, array })); - optionsMap_[opt] = &options_.back(); - return true; -} - -bool OptionsParser::addOption(int opt, KeyValueParser *parser, const char *help, - const char *name, bool array) -{ - if (!addOption(opt, OptionKeyValue, help, name, ArgumentRequired, - "key=value[,key=value,...]", array)) - return false; - - options_.back().keyValueParser = parser; - return true; -} - -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++; - } - } - - shortOptions[ids] = '\0'; - memset(&longOptions[idl], 0, sizeof(longOptions[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(); - return options; - } - - const Option &option = *optionsMap_[c]; - if (!options.parseValue(c, option, optarg)) { - parseValueError(option); - usage(); - return options; - } - } - - options.valid_ = true; - return 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 (option.isArray) - length += 4; - - 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) { - if (option.argument == ArgumentOptional) - argument += "[="; - else - argument += " "; - argument += option.argumentName; - if (option.argument == ArgumentOptional) - argument += "]"; - } - - if (option.isArray) - argument += " ..."; - - std::cerr << std::setw(indent) << std::left << argument; - - for (const char *help = option.help, *end = help; end; ) { - end = strchr(help, '\n'); - if (end) { - std::cerr << std::string(help, end - help + 1); - std::cerr << std::setw(indent) << " "; - help = end + 1; - } else { - std::cerr << help << std::endl; - } - } - - if (option.keyValueParser) - option.keyValueParser->usage(indent); - } -} - -void OptionsParser::parseValueError(const Option &option) -{ - std::string optionName; - - if (option.name) - optionName = "--" + std::string(option.name); - else - optionName = "-" + std::string(1, option.opt); - - std::cerr << "Can't parse " << option.typeName() - << " argument for option " << optionName << std::endl; -} diff --git a/src/cam/options.h b/src/cam/options.h deleted file mode 100644 index 5e346b47..00000000 --- a/src/cam/options.h +++ /dev/null @@ -1,147 +0,0 @@ -/* 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 <list> -#include <map> -#include <vector> - -class KeyValueParser; -class OptionValue; - -enum OptionArgument { - ArgumentNone, - ArgumentRequired, - ArgumentOptional, -}; - -enum OptionType { - OptionNone, - OptionInteger, - OptionString, - OptionKeyValue, -}; - -struct Option { - int opt; - OptionType type; - const char *name; - OptionArgument argument; - const char *argumentName; - const char *help; - KeyValueParser *keyValueParser; - bool isArray; - - bool hasShortOption() const { return isalnum(opt); } - bool hasLongOption() const { return name != nullptr; } - const char *typeName() const; -}; - -template<typename T> -class OptionsBase -{ -public: - OptionsBase() : valid_(false) {} - - bool empty() const; - bool valid() const; - bool isSet(const T &opt) const; - const OptionValue &operator[](const T &opt) const; - -private: - friend class KeyValueParser; - friend class OptionsParser; - - bool parseValue(const T &opt, const Option &option, const char *value); - - std::map<T, OptionValue> values_; - bool valid_; -}; - -class KeyValueParser -{ -public: - class Options : public OptionsBase<std::string> - { - }; - - bool addOption(const char *name, OptionType type, const char *help, - OptionArgument argument = ArgumentNone); - - Options parse(const char *arguments); - void usage(int indent); - -private: - std::map<std::string, Option> optionsMap_; -}; - -class OptionValue -{ -public: - enum ValueType { - ValueNone, - ValueInteger, - ValueString, - ValueKeyValue, - ValueArray, - }; - - OptionValue(); - OptionValue(int value); - OptionValue(const char *value); - OptionValue(const std::string &value); - OptionValue(const KeyValueParser::Options &value); - - void addValue(const OptionValue &value); - - ValueType type() const { return type_; } - - operator int() const; - operator std::string() const; - operator KeyValueParser::Options() const; - operator std::vector<OptionValue>() const; - - int toInteger() const; - std::string toString() const; - KeyValueParser::Options toKeyValues() const; - std::vector<OptionValue> toArray() const; - -private: - ValueType type_; - int integer_; - std::string string_; - KeyValueParser::Options keyValues_; - std::vector<OptionValue> array_; -}; - -class OptionsParser -{ -public: - class Options : public OptionsBase<int> - { - }; - - bool addOption(int opt, OptionType type, const char *help, - const char *name = nullptr, - OptionArgument argument = ArgumentNone, - const char *argumentName = nullptr, bool array = false); - bool addOption(int opt, KeyValueParser *parser, const char *help, - const char *name = nullptr, bool array = false); - - Options parse(int argc, char *argv[]); - void usage(); - -private: - void parseValueError(const Option &option); - - std::list<Option> options_; - std::map<unsigned int, Option *> optionsMap_; -}; - -#endif /* __CAM_OPTIONS_H__ */ |