// Copyright (C) 2016 xaizek <xaizek@posteo.net> // // This file is part of dit. // // dit is free software: you can redistribute it and/or modify // it under the terms of version 3 of the GNU Affero General Public // License as published by the Free Software Foundation. // // dit is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with dit. If not, see <http://www.gnu.org/licenses/>. // Boost Software License - Version 1.0 - August 17th, 2003 // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #ifndef DIT__UTILS__ARGS_HPP__ #define DIT__UTILS__ARGS_HPP__ #include <string> #include <vector> #include <boost/tokenizer.hpp> namespace detail { // arg_list_separator is based on boost::escaped_list_separator, hence the // license text above. This version treats consecutive separators as single one // and thus doesn't produce useless empty tokens (including trailing ones). // Unmatched quotation results in exception. template <class Char, class Traits = typename std::basic_string<Char>::traits_type> class arg_list_separator { typedef std::basic_string<Char, Traits> string_type; struct char_eq { Char e; char_eq(Char e) : e(e) { } bool operator()(Char c) { return Traits::eq(e, c); } }; public: explicit arg_list_separator(Char e = '\\', Char c = ',', Char q = '\"') : escape(1, e), c(1, c), quote(1, q) { } arg_list_separator(string_type e, string_type c, string_type q) : escape(e), c(c), quote(q) { } void reset() { } template <typename InputIterator, typename Token> bool operator()(InputIterator &next, InputIterator end, Token &tok) { Char inQuote = Char(); Char wasInQuote = Char(); tok = Token(); if (next == end) { return false; } while (next != end) { if (is_escape(*next)) { do_escape(next, end, tok); } else if (is_c(*next)) { if (!inQuote) { do { ++next; } while (next != end && is_c(*next)); if (tok.empty()) { if (next == end) { return false; } continue; } return true; } else { tok += *next; } } else if (is_quote(*next) && (!inQuote || *next == inQuote)) { wasInQuote = inQuote; inQuote = (inQuote ? Char() : *next); } else { tok += *next; } ++next; if (wasInQuote) { wasInQuote = Char(); if (is_c(*next)) { return true; } } } if (inQuote) { throw boost::escaped_list_error( std::string("incomplete quoted argument") ); } return true; } private: bool is_escape(Char e) { char_eq f(e); return std::find_if(escape.cbegin(), escape.cend(), f) != escape.cend(); } bool is_c(Char e) { char_eq f(e); return std::find_if(c.cbegin(), c.cend(), f) != c.cend(); } bool is_quote(Char e) { char_eq f(e); return std::find_if(quote.cbegin(), quote.cend(), f) != quote.cend(); } template <typename Iterator, typename Token> void do_escape(Iterator &next, Iterator end, Token &tok) { if (++next == end) { throw boost::escaped_list_error( std::string("cannot end with escape") ); } else if (Traits::eq(*next, 'n')) { tok += '\n'; } else if (is_quote(*next)) { tok += *next; } else if (is_c(*next)) { tok += *next; } else if (is_escape(*next)) { tok += *next; } else { throw boost::escaped_list_error( std::string("unknown escape sequence") ); } } private: string_type escape; string_type c; string_type quote; }; } /** * @brief Tokenize the command line, respecting escapes and quotes. * * @param line Line to parse. * * @returns Array of arguments. */ inline std::vector<std::string> breakIntoArgs(const std::string &line) { detail::arg_list_separator<char> sep("\\", " ", "\"'"); boost::tokenizer<decltype(sep)> tok(line, sep); return { tok.begin(), tok.end() }; } #endif // DIT__UTILS__ARGS_HPP__