summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ingen/Configuration.hpp159
-rw-r--r--src/Configuration.cpp179
-rw-r--r--src/ingen/main.cpp9
-rw-r--r--src/serialisation/Parser.cpp7
-rw-r--r--src/server/ingen_jack.cpp6
-rw-r--r--tests/ingen_test.cpp10
6 files changed, 340 insertions, 30 deletions
diff --git a/ingen/Configuration.hpp b/ingen/Configuration.hpp
index f45f3869..fac798d1 100644
--- a/ingen/Configuration.hpp
+++ b/ingen/Configuration.hpp
@@ -17,19 +17,172 @@
#ifndef INGEN_CONFIGURATION_HPP
#define INGEN_CONFIGURATION_HPP
-#include "raul/Configuration.hpp"
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "raul/Exception.hpp"
namespace Ingen {
-/** Ingen configuration (command line options).
+/** Ingen configuration (command line options and/or configuration file).
* @ingroup IngenShared
*/
-class Configuration : public Raul::Configuration {
+class Configuration {
public:
Configuration();
+
+ enum OptionType {
+ NOTHING,
+ BOOL,
+ INT,
+ STRING
+ };
+
+ class Value {
+ public:
+ Value() : _type(NOTHING) { _val._string = NULL; }
+ Value(bool v) : _type(BOOL) { _val._bool = v; }
+ Value(int v) : _type(INT) { _val._int = v; }
+
+ Value(const char* v) : _type(STRING) {
+ const size_t len = strlen(v);
+ _val._string = (char*)calloc(len + 1, 1);
+ memcpy(_val._string, v, len + 1);
+ }
+
+ Value(const std::string& v) : _type(STRING) {
+ _val._string = (char*)calloc(v.length() + 1, 1);
+ memcpy(_val._string, v.c_str(), v.length() + 1);
+ }
+
+ Value(const Value& copy)
+ : _type(copy._type)
+ , _val(copy._val)
+ {
+ if (_type == STRING) {
+ const size_t len = strlen(copy.get_string());
+ _val._string = (char*)malloc(len + 1);
+ memcpy(_val._string, copy.get_string(), len + 1);
+ }
+ }
+
+ Value& operator=(const Value& other)
+ {
+ if (&other == this) {
+ return *this;
+ }
+ if (_type == STRING) {
+ free(_val._string);
+ }
+ _type = other._type;
+ _val = other._val;
+ if (_type == STRING) {
+ const size_t len = strlen(other.get_string());
+ _val._string = (char*)malloc(len + 1);
+ memcpy(_val._string, other.get_string(), len + 1);
+ }
+ return *this;
+ }
+
+ ~Value() {
+ if (_type == STRING) {
+ free(_val._string);
+ }
+ }
+
+ inline OptionType type() const { return _type; }
+ inline bool is_valid() const { return _type != NOTHING; }
+
+ inline int32_t get_int() const { assert(_type == INT); return _val._int; }
+ inline bool get_bool() const { assert(_type == BOOL); return _val._bool; }
+ inline const char* get_string() const { assert(_type == STRING); return _val._string; }
+
+ private:
+ OptionType _type;
+ union {
+ bool _bool;
+ int32_t _int;
+ char* _string;
+ } _val;
+ };
+
+ Configuration& add(const std::string& name,
+ char letter,
+ const std::string& desc,
+ OptionType type,
+ const Value& value);
+
+ void print_usage(const std::string& program, std::ostream& os);
+
+ struct CommandLineError : public Exception {
+ explicit CommandLineError(const std::string& m) : Exception(m) {}
+ };
+
+ void parse(int argc, char** argv) throw (CommandLineError);
+
+ void print(std::ostream& os, const std::string mime_type="text/plain") const;
+
+ const Value& option(const std::string& long_name) const;
+ bool set(const std::string& long_name, const Value& value);
+
+ const std::list<std::string>& files() const { return _files; }
+
+private:
+ struct Option {
+ public:
+ Option(const std::string& n, char l, const std::string& d,
+ const OptionType type, const Value& def)
+ : name(n), letter(l), desc(d), type(type), default_value(def), value(def)
+ {}
+
+ std::string name;
+ char letter;
+ std::string desc;
+ OptionType type;
+ Value default_value;
+ Value value;
+ };
+
+ struct OptionNameOrder {
+ inline bool operator()(const Option& a, const Option& b) {
+ return a.name < b.name;
+ }
+ };
+
+ typedef std::map<std::string, Option> Options;
+ typedef std::map<char, std::string> ShortNames;
+ typedef std::list<std::string> Files;
+
+ int set_value_from_string(Configuration::Option& option, const std::string& value)
+ throw (Configuration::CommandLineError);
+
+ const std::string _shortdesc;
+ const std::string _desc;
+ Options _options;
+ ShortNames _short_names;
+ Files _files;
+ size_t _max_name_length;
};
} // namespace Ingen
+static inline std::ostream&
+operator<<(std::ostream& os, const Ingen::Configuration::Value& value)
+{
+ switch (value.type()) {
+ case Ingen::Configuration::NOTHING: return os << "(nil)";
+ case Ingen::Configuration::INT: return os << value.get_int();
+ case Ingen::Configuration::BOOL: return os << (value.get_bool() ? "true" : "false");
+ case Ingen::Configuration::STRING: return os << value.get_string();
+ }
+ return os;
+}
+
#endif // INGEN_CONFIGURATION_HPP
diff --git a/src/Configuration.cpp b/src/Configuration.cpp
index 2a5e69f7..9c9402c0 100644
--- a/src/Configuration.cpp
+++ b/src/Configuration.cpp
@@ -14,24 +14,27 @@
along with Ingen. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <iostream>
+
#include "ingen/Configuration.hpp"
namespace Ingen {
Configuration::Configuration()
- : Raul::Configuration(
- "A realtime modular audio processor.",
- "Ingen is a flexible modular system that be used in various ways.\n"
- "The engine can run as a stand-alone server controlled via network protocol,\n"
- "or internal to another process (e.g. the GUI). The GUI, or other\n"
- "clients, can communicate with the engine via any supported protocol, or host the\n"
- "engine in the same process. Many clients can connect to an engine at once.\n\n"
- "Examples:\n"
- " ingen -e # Run an engine, listen for connections\n"
- " ingen -g # Run a GUI, connect to running engine\n"
- " ingen -eg # Run an engine and a GUI in one process\n"
- " ingen -egl foo.ttl # Run an engine and a GUI and load a graph\n"
- " ingen -egl foo.ingen # Run an engine and a GUI and load a graph")
+ : _shortdesc("A realtime modular audio processor.")
+ , _desc(
+"Ingen is a flexible modular system that be used in various ways.\n"
+"The engine can run as a stand-alone server controlled via network protocol,\n"
+"or internal to another process (e.g. the GUI). The GUI, or other\n"
+"clients, can communicate with the engine via any supported protocol, or host the\n"
+"engine in the same process. Many clients can connect to an engine at once.\n\n"
+"Examples:\n"
+" ingen -e # Run an engine, listen for connections\n"
+" ingen -g # Run a GUI, connect to running engine\n"
+" ingen -eg # Run an engine and a GUI in one process\n"
+" ingen -egl foo.ttl # Run an engine and a GUI and load a graph\n"
+" ingen -egl foo.ingen # Run an engine and a GUI and load a graph")
+ , _max_name_length(0)
{
add("client-port", 'C', "Client port", INT, Value());
add("connect", 'c', "Connect to engine URI", STRING, Value("unix:///tmp/ingen.sock"));
@@ -50,4 +53,154 @@ Configuration::Configuration()
add("run", 'r', "Run script", STRING, Value());
}
+/** Add a configuration option.
+ *
+ * @param name Long name (without leading "--")
+ * @param letter Short name (without leading "-")
+ * @param desc Description
+ * @param type Type
+ * @param value Default value
+ */
+Configuration&
+Configuration::add(
+ const std::string& name,
+ char letter,
+ const std::string& desc,
+ const OptionType type,
+ const Value& value)
+{
+ assert(value.type() == type || value.type() == 0);
+ _max_name_length = std::max(_max_name_length, name.length());
+ _options.insert(make_pair(name, Option(name, letter, desc, type, value)));
+ if (letter != '\0') {
+ _short_names.insert(make_pair(letter, name));
+ }
+ return *this;
+}
+
+void
+Configuration::print_usage(const std::string& program, std::ostream& os)
+{
+ os << "Usage: " << program << " [OPTION]..." << std::endl;
+ os << _shortdesc << std::endl << std::endl;
+ os << _desc << std::endl << std::endl;
+ os << "Options:" << std::endl;
+ for (Options::iterator o = _options.begin(); o != _options.end(); ++o) {
+ Option& option = o->second;
+ os << " ";
+ if (option.letter != '\0')
+ os << "-" << option.letter << ", ";
+ else
+ os << " ";
+ os.width(_max_name_length + 4);
+ os << std::left << (std::string("--") + o->first);
+ os << option.desc << std::endl;
+ }
+}
+
+int
+Configuration::set_value_from_string(Configuration::Option& option,
+ const std::string& value)
+ throw (Configuration::CommandLineError)
+{
+ int intval = 0;
+ char* endptr = NULL;
+ switch (option.type) {
+ case INT:
+ intval = static_cast<int>(strtol(value.c_str(), &endptr, 10));
+ if (endptr && *endptr == '\0') {
+ option.value = Value(intval);
+ } else {
+ throw CommandLineError("option `" + option.name
+ + "' has non-integer value `" + value + "'");
+ }
+ break;
+ case STRING:
+ option.value = Value(value.c_str());
+ assert(option.value.type() == STRING);
+ break;
+ default:
+ throw CommandLineError(std::string("bad option type `--") + option.name + "'");
+ }
+ return EXIT_SUCCESS;
+}
+
+/** Parse command line arguments. */
+void
+Configuration::parse(int argc, char** argv) throw (Configuration::CommandLineError)
+{
+ for (int i = 1; i < argc; ++i) {
+ if (argv[i][0] != '-' || !strcmp(argv[i], "-")) {
+ _files.push_back(argv[i]);
+ } else if (argv[i][1] == '-') {
+ const std::string name = std::string(argv[i]).substr(2);
+ Options::iterator o = _options.find(name);
+ if (o == _options.end()) {
+ throw CommandLineError(std::string("unrecognized option `--") + name + "'");
+ }
+ if (o->second.type == BOOL) {
+ o->second.value = Value(true);
+ } else {
+ if (++i >= argc)
+ throw CommandLineError("missing value for `--" + name + "'");
+ set_value_from_string(o->second, argv[i]);
+ }
+ } else {
+ const size_t len = strlen(argv[i]);
+ for (size_t j = 1; j < len; ++j) {
+ char letter = argv[i][j];
+ ShortNames::iterator n = _short_names.find(letter);
+ if (n == _short_names.end())
+ throw CommandLineError(std::string("unrecognized option `-") + letter + "'");
+ Options::iterator o = _options.find(n->second);
+ if (j < len - 1) {
+ if (o->second.type != BOOL)
+ throw CommandLineError(std::string("missing value for `-") + letter + "'");
+ o->second.value = Value(true);
+ } else {
+ if (o->second.type == BOOL) {
+ o->second.value = Value(true);
+ } else {
+ if (++i >= argc)
+ throw CommandLineError(std::string("missing value for `-") + letter + "'");
+ set_value_from_string(o->second, argv[i]);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+Configuration::print(std::ostream& os, const std::string mime_type) const
+{
+ for (Options::const_iterator o = _options.begin(); o != _options.end(); ++o) {
+ const Option& option = o->second;
+ os << o->first << " = " << option.value << std::endl;
+ }
+}
+
+const Configuration::Value&
+Configuration::option(const std::string& long_name) const
+{
+ static const Value nil;
+ Options::const_iterator o = _options.find(long_name);
+ if (o == _options.end()) {
+ return nil;
+ } else {
+ return o->second.value;
+ }
+}
+
+bool
+Configuration::set(const std::string& long_name, const Value& value)
+{
+ Options::iterator o = _options.find(long_name);
+ if (o != _options.end()) {
+ o->second.value = value;
+ return true;
+ }
+ return false;
+}
+
} // namespace Ingen
diff --git a/src/ingen/main.cpp b/src/ingen/main.cpp
index 670cd514..99fa079a 100644
--- a/src/ingen/main.cpp
+++ b/src/ingen/main.cpp
@@ -23,20 +23,19 @@
#include <glibmm/thread.h>
#include <glibmm/timer.h>
-#include "raul/Configuration.hpp"
#include "raul/Path.hpp"
#include "raul/SharedPtr.hpp"
#include "raul/fmt.hpp"
#include "ingen_config.h"
+#include "ingen/Configuration.hpp"
#include "ingen/EngineBase.hpp"
#include "ingen/Interface.hpp"
-#include "ingen/serialisation/Parser.hpp"
-#include "ingen/Configuration.hpp"
#include "ingen/World.hpp"
-#include "ingen/runtime_paths.hpp"
#include "ingen/client/ThreadedSigClientInterface.hpp"
+#include "ingen/runtime_paths.hpp"
+#include "ingen/serialisation/Parser.hpp"
#ifdef WITH_BINDINGS
#include "bindings/ingen_bindings.hpp"
#endif
@@ -156,7 +155,7 @@ main(int argc, char** argv)
boost::optional<Raul::Path> parent;
boost::optional<Raul::Symbol> symbol;
- const Raul::Configuration::Value& path_option = conf.option("path");
+ const Configuration::Value& path_option = conf.option("path");
if (path_option.is_valid()) {
if (Raul::Path::is_valid(path_option.get_string())) {
const Raul::Path p(path_option.get_string());
diff --git a/src/serialisation/Parser.cpp b/src/serialisation/Parser.cpp
index a6036c32..0c15b0b4 100644
--- a/src/serialisation/Parser.cpp
+++ b/src/serialisation/Parser.cpp
@@ -229,7 +229,12 @@ parse_block(Ingen::World* world,
Sord::Iter i = model.find(subject, ingen_prototype, nil);
if (i.end() || i.get_object().type() != Sord::Node::URI) {
- world->log().error("Block missing mandatory ingen:prototype\n");
+ if (!i.end()) {
+ std::cerr << "type: " << i.get_object().type() << std::endl;
+ }
+ world->log().error(
+ Raul::fmt("Block %1% (%2%) missing mandatory ingen:prototype\n") %
+ subject.to_string() % path);
return boost::optional<Raul::Path>();
}
diff --git a/src/server/ingen_jack.cpp b/src/server/ingen_jack.cpp
index ec690756..dd713385 100644
--- a/src/server/ingen_jack.cpp
+++ b/src/server/ingen_jack.cpp
@@ -17,10 +17,10 @@
#include <string>
#include "ingen/Configuration.hpp"
+#include "ingen/Configuration.hpp"
+#include "ingen/Log.hpp"
#include "ingen/Module.hpp"
#include "ingen/World.hpp"
-#include "ingen/Log.hpp"
-#include "raul/Configuration.hpp"
#include "JackDriver.hpp"
#include "Engine.hpp"
@@ -37,7 +37,7 @@ struct IngenJackModule : public Ingen::Module {
Server::JackDriver* driver = new Server::JackDriver(
*(Server::Engine*)world->engine().get());
- const Raul::Configuration::Value& s = world->conf().option("jack-server");
+ const Configuration::Value& s = world->conf().option("jack-server");
const std::string server_name = s.is_valid() ? s.get_string() : "";
driver->attach(server_name,
world->conf().option("jack-client").get_string(),
diff --git a/tests/ingen_test.cpp b/tests/ingen_test.cpp
index 62ee502a..4958142b 100644
--- a/tests/ingen_test.cpp
+++ b/tests/ingen_test.cpp
@@ -27,7 +27,6 @@
#include <glibmm/thread.h>
#include <glibmm/timer.h>
-#include "raul/Configuration.hpp"
#include "raul/Path.hpp"
#include "raul/SharedPtr.hpp"
#include "raul/Thread.hpp"
@@ -38,16 +37,17 @@
#include "ingen_config.h"
-#include "ingen/EngineBase.hpp"
-#include "ingen/Interface.hpp"
-#include "ingen/serialisation/Parser.hpp"
#include "ingen/AtomReader.hpp"
#include "ingen/AtomWriter.hpp"
#include "ingen/Configuration.hpp"
+#include "ingen/Configuration.hpp"
+#include "ingen/EngineBase.hpp"
+#include "ingen/Interface.hpp"
#include "ingen/URIMap.hpp"
#include "ingen/World.hpp"
-#include "ingen/runtime_paths.hpp"
#include "ingen/client/ThreadedSigClientInterface.hpp"
+#include "ingen/runtime_paths.hpp"
+#include "ingen/serialisation/Parser.hpp"
#ifdef WITH_BINDINGS
#include "bindings/ingen_bindings.hpp"
#endif