1 /*  Copyright (C) 2012-2021 by László Nagy
2     This file is part of Bear.
3 
4     Bear is a tool to generate compilation database for clang tooling.
5 
6     Bear is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     Bear is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "libshell/Command.h"
21 
22 #include <regex>
23 #include <stdexcept>
24 
25 namespace sh {
26 
escape(const std::string & input)27     std::string escape(const std::string& input)
28     {
29         if (input.empty()) {
30             return "''";
31         }
32 
33         const std::regex ESCAPE_PATTERN(R"#(([^A-Za-z0-9_\-.,:/@\n]))#");
34         const std::regex LINE_FEED(R"#(\n)#");
35 
36         auto output = std::regex_replace(input, ESCAPE_PATTERN, "\\$1");
37         return std::regex_replace(output, LINE_FEED, "'\n'");
38     }
39 
join(const std::list<std::string> & arguments)40     std::string join(const std::list<std::string>& arguments)
41     {
42         std::string result;
43         for (auto it = arguments.begin(); it != arguments.end(); ++it) {
44             if (it != arguments.begin()) {
45                 result += " ";
46             }
47             result += escape(*it);
48         }
49         return result;
50     }
51 
split(const std::string & input)52     rust::Result<std::list<std::string>> split(const std::string& input)
53     {
54         const std::regex MAIN_PATTERN(R"#((?:\s*(?:([^\s\\'"]+)|'([^']*)'|"((?:[^"\\]|\\.)*)"|(\\.?)|(\S))(\s|$)?))#",
55                                       std::regex::ECMAScript);
56         const std::regex ESCAPE_PATTERN(R"#(\\(.))#");
57         const std::regex METACHAR_PATTERN(R"(\\([$`"\\\n]))");
58 
59         std::list<std::string> words;
60         std::string field;
61 
62         auto input_begin = std::sregex_iterator(input.begin(), input.end(), MAIN_PATTERN);
63         auto input_end = std::sregex_iterator();
64         for (auto it = input_begin; it != input_end; ++it) {
65             if (it->ready()) {
66                 if (it->operator[](1).matched) {
67                     field += it->str(1);
68                 } else if (it->operator[](2).matched) {
69                     field += it->str(2);
70                 } else if (it->operator[](3).matched) {
71                     field += std::regex_replace(it->str(3), METACHAR_PATTERN, "$1");
72                 } else if (it->operator[](4).matched) {
73                     field += std::regex_replace(it->str(4), ESCAPE_PATTERN, "$1");
74                 } else if (it->operator[](5).matched) {
75                     return rust::Err(std::runtime_error("Mismatched quotes."));
76                 }
77 
78                 if (it->operator[](6).matched) {
79                     words.push_back(field);
80                     field.clear();
81                 }
82             }
83         }
84         return rust::Ok(words);
85     }
86 }
87