diff --git a/Util/include/Poco/Util/OptionProcessor.h b/Util/include/Poco/Util/OptionProcessor.h index 45e24351b..ae345c39b 100644 --- a/Util/include/Poco/Util/OptionProcessor.h +++ b/Util/include/Poco/Util/OptionProcessor.h @@ -55,13 +55,13 @@ class Util_API OptionProcessor /// An OptionProcessor is used to process the command line /// arguments of an application. /// - /// The process() method takes an argument from the command line. - /// If that argument starts with an option prefix, the argument - /// is further processed. Otherwise, the argument is ignored and - /// false is ignored. The argument must match one of the options - /// given in the OptionSet that is passed to the OptionProcessor - /// with the constructor. If an option is part of a group, at most - /// one option of the group can be passed to the OptionProcessor. + /// The process() method takes an argument from the command line. + /// If that argument starts with an option prefix, the argument + /// is further processed. Otherwise, the argument is ignored and + /// false is returned. The argument must match one of the options + /// given in the OptionSet that is passed to the OptionProcessor + /// with the constructor. If an option is part of a group, at most + /// one option of the group can be passed to the OptionProcessor. /// Otherwise an IncompatibleOptionsException is thrown. /// If the same option is given multiple times, but the option /// is not repeatable, a DuplicateOptionException is thrown. @@ -79,12 +79,22 @@ class Util_API OptionProcessor /// by a short option name, or another dash, followed by a (partial) /// long option name. /// In default mode, the option prefix is a slash '/', followed by - /// a (partial) long option name. - /// If the special option '--' is encountered in Unix mode, all following - /// options are ignored. + /// a (partial) long option name. + /// If the special option '--' is encountered in Unix mode, all following + /// options are ignored. + /// + /// Option arguments can be specified in three ways. If a Unix short option + /// ("-o") is given, the argument directly follows the option name, without + /// any delimiting character or space ("-ovalue"). In default option mode, or if a + /// Unix long option ("--option") is given, the option argument is + /// delimited from the option name with either an equal sign ('=') or + /// a colon (':'), as in "--option=value" or "/option:value". Finally, + /// a required option argument can be specified on the command line after the + /// option, delimited with a space, as in "--option value" or "-o value". + /// The latter only works for required option arguments, not optional ones. { public: - OptionProcessor(const OptionSet& options); + OptionProcessor(const OptionSet& options); /// Creates the OptionProcessor, using the given OptionSet. ~OptionProcessor(); @@ -127,9 +137,10 @@ private: const OptionSet& _options; bool _unixStyle; - bool _ignore; - std::set _groups; - std::set _specifiedOptions; + bool _ignore; + std::set _groups; + std::set _specifiedOptions; + std::string _deferredOption; }; diff --git a/Util/include/Poco/Util/XMLConfiguration.h b/Util/include/Poco/Util/XMLConfiguration.h index ae78a82f6..e88dc0107 100644 --- a/Util/include/Poco/Util/XMLConfiguration.h +++ b/Util/include/Poco/Util/XMLConfiguration.h @@ -86,35 +86,68 @@ class Util_API XMLConfiguration: public AbstractConfiguration /// prop5[1] -> value6 /// prop5[@id=first] -> value5 /// prop5[@id='second'] -> value6 - /// - /// Enumerating attributes is not supported. - /// Calling keys("prop3.prop4") will return an empty range. + /// + /// Enumerating attributes is not supported. + /// Calling keys("prop3.prop4") will return an empty range. + /// + /// As a special feature, the delimiter character used to delimit + /// property names can be changed to something other than period ('.') by + /// passing the desired character to the constructor. This allows + /// working with XML documents having element names with periods + /// in them. { public: - XMLConfiguration(); - /// Creates an empty XMLConfiguration. + XMLConfiguration(); + /// Creates an empty XMLConfiguration. - XMLConfiguration(Poco::XML::InputSource* pInputSource); - /// Creates an XMLConfiguration and loads the XML document from - /// the given InputSource. + XMLConfiguration(char delim); + /// Creates an empty XMLConfiguration, using the given + /// delimiter char instead of the default '.'. - XMLConfiguration(std::istream& istr); - /// Creates an XMLConfiguration and loads the XML document from - /// the given stream. + XMLConfiguration(Poco::XML::InputSource* pInputSource); + /// Creates an XMLConfiguration and loads the XML document from + /// the given InputSource. - XMLConfiguration(const std::string& path); - /// Creates an XMLConfiguration and loads the XML document from - /// the given path. + XMLConfiguration(Poco::XML::InputSource* pInputSource, char delim); + /// Creates an XMLConfiguration and loads the XML document from + /// the given InputSource. Uses the given delimiter char instead + /// of the default '.'. - XMLConfiguration(const Poco::XML::Document* pDocument); - /// Creates the XMLConfiguration using the given XML document. - - XMLConfiguration(const Poco::XML::Node* pNode); - /// Creates the XMLConfiguration using the given XML node. + XMLConfiguration(std::istream& istr); + /// Creates an XMLConfiguration and loads the XML document from + /// the given stream. - void load(Poco::XML::InputSource* pInputSource); - /// Loads the XML document containing the configuration data - /// from the given InputSource. + XMLConfiguration(std::istream& istr, char delim); + /// Creates an XMLConfiguration and loads the XML document from + /// the given stream. Uses the given delimiter char instead + /// of the default '.'. + + XMLConfiguration(const std::string& path); + /// Creates an XMLConfiguration and loads the XML document from + /// the given path. + + XMLConfiguration(const std::string& path, char delim); + /// Creates an XMLConfiguration and loads the XML document from + /// the given path. Uses the given delimiter char instead + /// of the default '.'. + + XMLConfiguration(const Poco::XML::Document* pDocument); + /// Creates the XMLConfiguration using the given XML document. + + XMLConfiguration(const Poco::XML::Document* pDocument, char delim); + /// Creates the XMLConfiguration using the given XML document. + /// Uses the given delimiter char instead of the default '.'. + + XMLConfiguration(const Poco::XML::Node* pNode); + /// Creates the XMLConfiguration using the given XML node. + + XMLConfiguration(const Poco::XML::Node* pNode, char delim); + /// Creates the XMLConfiguration using the given XML node. + /// Uses the given delimiter char instead of the default '.'. + + void load(Poco::XML::InputSource* pInputSource); + /// Loads the XML document containing the configuration data + /// from the given InputSource. void load(std::istream& istr); /// Loads the XML document containing the configuration data @@ -164,16 +197,17 @@ protected: ~XMLConfiguration(); private: - const Poco::XML::Node* findNode(const std::string& key) const; - Poco::XML::Node* findNode(const std::string& key); - static Poco::XML::Node* findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create = false); - static Poco::XML::Node* findElement(const std::string& name, Poco::XML::Node* pNode, bool create); - static Poco::XML::Node* findElement(int index, Poco::XML::Node* pNode, bool create); - static Poco::XML::Node* findElement(const std::string& attr, const std::string& value, Poco::XML::Node* pNode); + const Poco::XML::Node* findNode(const std::string& key) const; + Poco::XML::Node* findNode(const std::string& key); + Poco::XML::Node* findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create = false) const; + static Poco::XML::Node* findElement(const std::string& name, Poco::XML::Node* pNode, bool create); + static Poco::XML::Node* findElement(int index, Poco::XML::Node* pNode, bool create); + static Poco::XML::Node* findElement(const std::string& attr, const std::string& value, Poco::XML::Node* pNode); static Poco::XML::Node* findAttribute(const std::string& name, Poco::XML::Node* pNode, bool create); - Poco::XML::AutoPtr _pRoot; - Poco::XML::AutoPtr _pDocument; + Poco::XML::AutoPtr _pRoot; + Poco::XML::AutoPtr _pDocument; + char _delim; }; diff --git a/Util/src/Application.cpp b/Util/src/Application.cpp index 43e0703a8..7d7ce6372 100644 --- a/Util/src/Application.cpp +++ b/Util/src/Application.cpp @@ -116,15 +116,15 @@ void Application::setup() _pConfig->add(new SystemConfiguration, PRIO_SYSTEM, false, false); _pConfig->add(new MapConfiguration, PRIO_APPLICATION, true, false); - - addSubsystem(new LoggingSubsystem); - + + addSubsystem(new LoggingSubsystem); + #if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS) - _workingDirAtLaunch = Path::current(); + _workingDirAtLaunch = Path::current(); - #if !defined(_DEBUG) - Poco::SignalHandler::install(); - #endif + #if !defined(_DEBUG) + Poco::SignalHandler::install(); + #endif #else setUnixOptions(false); #endif @@ -368,13 +368,13 @@ void Application::processOptions() while (it != _args.end() && !_stopOptionsProcessing) { std::string name; - std::string value; - if (processor.process(*it, name, value)) - { - if (!name.empty()) // "--" option to end options processing - { - handleOption(name, value); - } + std::string value; + if (processor.process(*it, name, value)) + { + if (!name.empty()) // "--" option to end options processing or deferred argument + { + handleOption(name, value); + } it = _args.erase(it); } else ++it; @@ -393,19 +393,19 @@ void Application::getApplicationPath(Poco::Path& appPath) const if (path.isAbsolute()) { appPath = path; - } - else - { - appPath = _workingDirAtLaunch; - appPath.append(path); - } - } - else - { - if (!Path::find(Environment::get("PATH"), _command, appPath)) - appPath = Path(_workingDirAtLaunch, _command); - appPath.makeAbsolute(); - } + } + else + { + appPath = _workingDirAtLaunch; + appPath.append(path); + } + } + else + { + if (!Path::find(Environment::get("PATH"), _command, appPath)) + appPath = Path(_workingDirAtLaunch, _command); + appPath.makeAbsolute(); + } #elif defined(POCO_OS_FAMILY_WINDOWS) #if defined(POCO_WIN32_UTF8) && !defined(POCO_NO_WSTRING) wchar_t path[1024]; diff --git a/Util/src/HelpFormatter.cpp b/Util/src/HelpFormatter.cpp index 757a3290a..ec7d45ecd 100644 --- a/Util/src/HelpFormatter.cpp +++ b/Util/src/HelpFormatter.cpp @@ -173,12 +173,20 @@ int HelpFormatter::calcIndent() const void HelpFormatter::formatOptions(std::ostream& ostr) const { int optWidth = calcIndent(); - for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it) - { - formatOption(ostr, *it, optWidth); - formatText(ostr, it->description(), _indent, optWidth); - ostr << '\n'; - } + for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it) + { + formatOption(ostr, *it, optWidth); + if (_indent < optWidth) + { + ostr << '\n' << std::string(_indent, ' '); + formatText(ostr, it->description(), _indent, _indent); + } + else + { + formatText(ostr, it->description(), _indent, optWidth); + } + ostr << '\n'; + } } diff --git a/Util/src/OptionProcessor.cpp b/Util/src/OptionProcessor.cpp index 2d49c748d..09d63a1fc 100644 --- a/Util/src/OptionProcessor.cpp +++ b/Util/src/OptionProcessor.cpp @@ -65,12 +65,16 @@ void OptionProcessor::setUnixStyle(bool flag) bool OptionProcessor::process(const std::string& argument, std::string& optionName, std::string& optionArg) { - if (!_ignore) - { - if (_unixStyle) - return processUnix(argument, optionName, optionArg); - else - return processDefault(argument, optionName, optionArg); + optionName.clear(); + optionArg.clear(); + if (!_ignore) + { + if (!_deferredOption.empty()) + return processCommon(argument, false, optionName, optionArg); + else if (_unixStyle) + return processUnix(argument, optionName, optionArg); + else + return processDefault(argument, optionName, optionArg); } return false; } @@ -80,9 +84,15 @@ void OptionProcessor::checkRequired() const { for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it) { - if (it->required() && _specifiedOptions.find(it->fullName()) == _specifiedOptions.end()) - throw MissingOptionException(it->fullName()); - } + if (it->required() && _specifiedOptions.find(it->fullName()) == _specifiedOptions.end()) + throw MissingOptionException(it->fullName()); + } + if (!_deferredOption.empty()) + { + std::string optionArg; + const Option& option = _options.getOption(_deferredOption, false); + option.process(_deferredOption, optionArg); // will throw MissingArgumentException + } } @@ -133,9 +143,20 @@ bool OptionProcessor::processDefault(const std::string& argument, std::string& o bool OptionProcessor::processCommon(const std::string& optionStr, bool isShort, std::string& optionName, std::string& optionArg) { - if (optionStr.empty()) throw EmptyOptionException(); - const Option& option = _options.getOption(optionStr, isShort); - const std::string& group = option.group(); + if (!_deferredOption.empty()) + { + const Option& option = _options.getOption(_deferredOption, false); + std::string optionWithArg(_deferredOption); + _deferredOption.clear(); + optionWithArg += '='; + optionWithArg += optionStr; + option.process(optionWithArg, optionArg); + optionName = option.fullName(); + return true; + } + if (optionStr.empty()) throw EmptyOptionException(); + const Option& option = _options.getOption(optionStr, isShort); + const std::string& group = option.group(); if (!group.empty()) { if (_groups.find(group) != _groups.end()) @@ -143,12 +164,17 @@ bool OptionProcessor::processCommon(const std::string& optionStr, bool isShort, else _groups.insert(group); } - if (_specifiedOptions.find(option.fullName()) != _specifiedOptions.end() && !option.repeatable()) - throw DuplicateOptionException(option.fullName()); - _specifiedOptions.insert(option.fullName()); - option.process(optionStr, optionArg); - optionName = option.fullName(); - return true; + if (_specifiedOptions.find(option.fullName()) != _specifiedOptions.end() && !option.repeatable()) + throw DuplicateOptionException(option.fullName()); + _specifiedOptions.insert(option.fullName()); + if (option.argumentRequired() && ((!isShort && optionStr.find_first_of(":=") == std::string::npos) || (isShort && optionStr.length() == option.shortName().length()))) + { + _deferredOption = option.fullName(); + return true; + } + option.process(optionStr, optionArg); + optionName = option.fullName(); + return true; } diff --git a/Util/src/XMLConfiguration.cpp b/Util/src/XMLConfiguration.cpp index b0f2e660a..5fbe8f8db 100644 --- a/Util/src/XMLConfiguration.cpp +++ b/Util/src/XMLConfiguration.cpp @@ -51,38 +51,85 @@ namespace Poco { namespace Util { -XMLConfiguration::XMLConfiguration() +XMLConfiguration::XMLConfiguration(): + _delim('.') { } -XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource) +XMLConfiguration::XMLConfiguration(char delim): + _delim(delim) { - load(pInputSource); } -XMLConfiguration::XMLConfiguration(std::istream& istr) +XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource): + _delim('.') { - load(istr); + load(pInputSource); } -XMLConfiguration::XMLConfiguration(const std::string& path) +XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource, char delim): + _delim(delim) { - load(path); + load(pInputSource); } -XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument) +XMLConfiguration::XMLConfiguration(std::istream& istr): + _delim('.') { - load(pDocument); + load(istr); } - -XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode) + +XMLConfiguration::XMLConfiguration(std::istream& istr, char delim): + _delim(delim) { - load(pNode); + load(istr); +} + + +XMLConfiguration::XMLConfiguration(const std::string& path): + _delim('.') +{ + load(path); +} + + +XMLConfiguration::XMLConfiguration(const std::string& path, char delim): + _delim(delim) +{ + load(path); +} + + +XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument): + _delim('.') +{ + load(pDocument); +} + + +XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument, char delim): + _delim(delim) +{ + load(pDocument); +} + + +XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode): + _delim('.') +{ + load(pNode); +} + + +XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode, char delim): + _delim(delim) +{ + load(pNode); } @@ -294,10 +341,10 @@ Poco::XML::Node* XMLConfiguration::findNode(const std::string& key) } -Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create) +Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create) const { - if (pNode && it != end) - { + if (pNode && it != end) + { if (*it == '[') { ++it; @@ -336,15 +383,15 @@ Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, con if (it != end) ++it; return findNode(it, end, findElement(Poco::NumberParser::parse(index), pNode, create), create); } - } - else - { - while (it != end && *it == '.') ++it; - std::string key; - while (it != end && *it != '.' && *it != '[') key += *it++; - return findNode(it, end, findElement(key, pNode, create), create); - } - } + } + else + { + while (it != end && *it == _delim) ++it; + std::string key; + while (it != end && *it != _delim && *it != '[') key += *it++; + return findNode(it, end, findElement(key, pNode, create), create); + } + } else return pNode; }