1 //===-- ArgsTest.cpp ------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "gtest/gtest.h"
10 
11 #include "lldb/Utility/Args.h"
12 #include "lldb/Utility/FileSpec.h"
13 #include "lldb/Utility/StringList.h"
14 
15 #include <limits>
16 #include <sstream>
17 
18 using namespace lldb_private;
19 
TEST(ArgsTest,TestSingleArg)20 TEST(ArgsTest, TestSingleArg) {
21   Args args;
22   args.SetCommandString("arg");
23   EXPECT_EQ(1u, args.GetArgumentCount());
24   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg");
25 }
26 
TEST(ArgsTest,TestSingleQuotedArgWithSpace)27 TEST(ArgsTest, TestSingleQuotedArgWithSpace) {
28   Args args;
29   args.SetCommandString("\"arg with space\"");
30   EXPECT_EQ(1u, args.GetArgumentCount());
31   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space");
32 }
33 
TEST(ArgsTest,TestSingleArgWithQuotedSpace)34 TEST(ArgsTest, TestSingleArgWithQuotedSpace) {
35   Args args;
36   args.SetCommandString("arg\\ with\\ space");
37   EXPECT_EQ(1u, args.GetArgumentCount());
38   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space");
39 }
40 
TEST(ArgsTest,TestTrailingBackslash)41 TEST(ArgsTest, TestTrailingBackslash) {
42   Args args;
43   args.SetCommandString("arg\\");
44   EXPECT_EQ(1u, args.GetArgumentCount());
45   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg\\");
46 }
47 
TEST(ArgsTest,TestQuotedTrailingBackslash)48 TEST(ArgsTest, TestQuotedTrailingBackslash) {
49   Args args;
50   args.SetCommandString("\"arg\\");
51   EXPECT_EQ(1u, args.GetArgumentCount());
52   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg\\");
53 }
54 
TEST(ArgsTest,TestUnknownEscape)55 TEST(ArgsTest, TestUnknownEscape) {
56   Args args;
57   args.SetCommandString("arg\\y");
58   EXPECT_EQ(1u, args.GetArgumentCount());
59   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg\\y");
60 }
61 
TEST(ArgsTest,TestQuotedUnknownEscape)62 TEST(ArgsTest, TestQuotedUnknownEscape) {
63   Args args;
64   args.SetCommandString("\"arg\\y");
65   EXPECT_EQ(1u, args.GetArgumentCount());
66   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg\\y");
67 }
68 
TEST(ArgsTest,TestMultipleArgs)69 TEST(ArgsTest, TestMultipleArgs) {
70   Args args;
71   args.SetCommandString("this has multiple args");
72   EXPECT_EQ(4u, args.GetArgumentCount());
73   EXPECT_STREQ(args.GetArgumentAtIndex(0), "this");
74   EXPECT_STREQ(args.GetArgumentAtIndex(1), "has");
75   EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple");
76   EXPECT_STREQ(args.GetArgumentAtIndex(3), "args");
77 }
78 
TEST(ArgsTest,TestOverwriteArgs)79 TEST(ArgsTest, TestOverwriteArgs) {
80   Args args;
81   args.SetCommandString("this has multiple args");
82   EXPECT_EQ(4u, args.GetArgumentCount());
83   args.SetCommandString("arg");
84   EXPECT_EQ(1u, args.GetArgumentCount());
85   EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg");
86 }
87 
TEST(ArgsTest,TestAppendArg)88 TEST(ArgsTest, TestAppendArg) {
89   Args args;
90   args.SetCommandString("first_arg");
91   EXPECT_EQ(1u, args.GetArgumentCount());
92   args.AppendArgument(llvm::StringRef("second_arg"));
93   EXPECT_EQ(2u, args.GetArgumentCount());
94   EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg");
95   EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg");
96 }
97 
TEST(ArgsTest,TestInsertArg)98 TEST(ArgsTest, TestInsertArg) {
99   Args args;
100   args.AppendArgument("1");
101   args.AppendArgument("2");
102   args.AppendArgument("3");
103   args.InsertArgumentAtIndex(1, "1.5");
104   args.InsertArgumentAtIndex(4, "3.5");
105 
106   ASSERT_EQ(5u, args.GetArgumentCount());
107   EXPECT_STREQ("1", args.GetArgumentAtIndex(0));
108   EXPECT_STREQ("1.5", args.GetArgumentAtIndex(1));
109   EXPECT_STREQ("2", args.GetArgumentAtIndex(2));
110   EXPECT_STREQ("3", args.GetArgumentAtIndex(3));
111   EXPECT_STREQ("3.5", args.GetArgumentAtIndex(4));
112 }
113 
TEST(ArgsTest,TestArgv)114 TEST(ArgsTest, TestArgv) {
115   Args args;
116   EXPECT_EQ(nullptr, args.GetArgumentVector());
117 
118   args.AppendArgument("1");
119   EXPECT_NE(nullptr, args.GetArgumentVector()[0]);
120   EXPECT_EQ(nullptr, args.GetArgumentVector()[1]);
121 
122   args.AppendArgument("2");
123   EXPECT_NE(nullptr, args.GetArgumentVector()[0]);
124   EXPECT_NE(nullptr, args.GetArgumentVector()[1]);
125   EXPECT_EQ(nullptr, args.GetArgumentVector()[2]);
126 
127   args.AppendArgument("3");
128   EXPECT_NE(nullptr, args.GetArgumentVector()[0]);
129   EXPECT_NE(nullptr, args.GetArgumentVector()[1]);
130   EXPECT_NE(nullptr, args.GetArgumentVector()[2]);
131   EXPECT_EQ(nullptr, args.GetArgumentVector()[3]);
132 
133   args.InsertArgumentAtIndex(1, "1.5");
134   EXPECT_NE(nullptr, args.GetArgumentVector()[0]);
135   EXPECT_NE(nullptr, args.GetArgumentVector()[1]);
136   EXPECT_NE(nullptr, args.GetArgumentVector()[2]);
137   EXPECT_NE(nullptr, args.GetArgumentVector()[3]);
138   EXPECT_EQ(nullptr, args.GetArgumentVector()[4]);
139 
140   args.InsertArgumentAtIndex(4, "3.5");
141   EXPECT_NE(nullptr, args.GetArgumentVector()[0]);
142   EXPECT_NE(nullptr, args.GetArgumentVector()[1]);
143   EXPECT_NE(nullptr, args.GetArgumentVector()[2]);
144   EXPECT_NE(nullptr, args.GetArgumentVector()[3]);
145   EXPECT_NE(nullptr, args.GetArgumentVector()[4]);
146   EXPECT_EQ(nullptr, args.GetArgumentVector()[5]);
147 }
148 
TEST(ArgsTest,StringListConstructor)149 TEST(ArgsTest, StringListConstructor) {
150   StringList list;
151   list << "foo"
152        << "bar"
153        << "baz";
154   Args args(list);
155   ASSERT_EQ(3u, args.GetArgumentCount());
156   EXPECT_EQ("foo", args[0].ref());
157   EXPECT_EQ("bar", args[1].ref());
158   EXPECT_EQ("baz", args[2].ref());
159 }
160 
TEST(ArgsTest,GetQuotedCommandString)161 TEST(ArgsTest, GetQuotedCommandString) {
162   Args args;
163   const char *str = "process launch -o stdout.txt -- \"a b c\"";
164   args.SetCommandString(str);
165 
166   std::string stdstr;
167   ASSERT_TRUE(args.GetQuotedCommandString(stdstr));
168   EXPECT_EQ(str, stdstr);
169 }
170 
TEST(ArgsTest,BareSingleQuote)171 TEST(ArgsTest, BareSingleQuote) {
172   Args args;
173   args.SetCommandString("a\\'b");
174   EXPECT_EQ(1u, args.GetArgumentCount());
175 
176   EXPECT_STREQ("a'b", args.GetArgumentAtIndex(0));
177 }
178 
TEST(ArgsTest,DoubleQuotedItem)179 TEST(ArgsTest, DoubleQuotedItem) {
180   Args args;
181   args.SetCommandString("\"a b c\"");
182   EXPECT_EQ(1u, args.GetArgumentCount());
183 
184   EXPECT_STREQ("a b c", args.GetArgumentAtIndex(0));
185 }
186 
TEST(ArgsTest,AppendArguments)187 TEST(ArgsTest, AppendArguments) {
188   Args args;
189   const char *argv[] = {"1", "2", nullptr};
190   const char *argv2[] = {"3", "4", nullptr};
191 
192   args.AppendArguments(argv);
193   ASSERT_EQ(2u, args.GetArgumentCount());
194   EXPECT_STREQ("1", args.GetArgumentVector()[0]);
195   EXPECT_STREQ("2", args.GetArgumentVector()[1]);
196   EXPECT_EQ(nullptr, args.GetArgumentVector()[2]);
197   EXPECT_STREQ("1", args.GetArgumentAtIndex(0));
198   EXPECT_STREQ("2", args.GetArgumentAtIndex(1));
199 
200   args.AppendArguments(argv2);
201   ASSERT_EQ(4u, args.GetArgumentCount());
202   EXPECT_STREQ("1", args.GetArgumentVector()[0]);
203   EXPECT_STREQ("2", args.GetArgumentVector()[1]);
204   EXPECT_STREQ("3", args.GetArgumentVector()[2]);
205   EXPECT_STREQ("4", args.GetArgumentVector()[3]);
206   EXPECT_EQ(nullptr, args.GetArgumentVector()[4]);
207   EXPECT_STREQ("1", args.GetArgumentAtIndex(0));
208   EXPECT_STREQ("2", args.GetArgumentAtIndex(1));
209   EXPECT_STREQ("3", args.GetArgumentAtIndex(2));
210   EXPECT_STREQ("4", args.GetArgumentAtIndex(3));
211 }
212 
TEST(ArgsTest,GetArgumentArrayRef)213 TEST(ArgsTest, GetArgumentArrayRef) {
214   Args args("foo bar");
215   auto ref = args.GetArgumentArrayRef();
216   ASSERT_EQ(2u, ref.size());
217   EXPECT_STREQ("foo", ref[0]);
218   EXPECT_STREQ("bar", ref[1]);
219 }
220 
TEST(ArgsTest,EscapeLLDBCommandArgument)221 TEST(ArgsTest, EscapeLLDBCommandArgument) {
222   const std::string foo = "foo'";
223   EXPECT_EQ("foo\\'", Args::EscapeLLDBCommandArgument(foo, '\0'));
224   EXPECT_EQ("foo'", Args::EscapeLLDBCommandArgument(foo, '\''));
225   EXPECT_EQ("foo'", Args::EscapeLLDBCommandArgument(foo, '`'));
226   EXPECT_EQ("foo'", Args::EscapeLLDBCommandArgument(foo, '"'));
227 
228   const std::string bar = "bar\"";
229   EXPECT_EQ("bar\\\"", Args::EscapeLLDBCommandArgument(bar, '\0'));
230   EXPECT_EQ("bar\"", Args::EscapeLLDBCommandArgument(bar, '\''));
231   EXPECT_EQ("bar\"", Args::EscapeLLDBCommandArgument(bar, '`'));
232   EXPECT_EQ("bar\\\"", Args::EscapeLLDBCommandArgument(bar, '"'));
233 
234   const std::string baz = "baz`";
235   EXPECT_EQ("baz\\`", Args::EscapeLLDBCommandArgument(baz, '\0'));
236   EXPECT_EQ("baz`", Args::EscapeLLDBCommandArgument(baz, '\''));
237   EXPECT_EQ("baz`", Args::EscapeLLDBCommandArgument(baz, '`'));
238   EXPECT_EQ("baz\\`", Args::EscapeLLDBCommandArgument(baz, '"'));
239 
240   const std::string quux = "quux\t";
241   EXPECT_EQ("quux\\\t", Args::EscapeLLDBCommandArgument(quux, '\0'));
242   EXPECT_EQ("quux\t", Args::EscapeLLDBCommandArgument(quux, '\''));
243   EXPECT_EQ("quux\t", Args::EscapeLLDBCommandArgument(quux, '`'));
244   EXPECT_EQ("quux\t", Args::EscapeLLDBCommandArgument(quux, '"'));
245 }
246 
TEST(ArgsTest,ReplaceArgumentAtIndexShort)247 TEST(ArgsTest, ReplaceArgumentAtIndexShort) {
248   Args args;
249   args.SetCommandString("foo ba b");
250   args.ReplaceArgumentAtIndex(0, "f");
251   EXPECT_EQ(3u, args.GetArgumentCount());
252   EXPECT_STREQ(args.GetArgumentAtIndex(0), "f");
253 }
254 
TEST(ArgsTest,ReplaceArgumentAtIndexEqual)255 TEST(ArgsTest, ReplaceArgumentAtIndexEqual) {
256   Args args;
257   args.SetCommandString("foo ba b");
258   args.ReplaceArgumentAtIndex(0, "bar");
259   EXPECT_EQ(3u, args.GetArgumentCount());
260   EXPECT_STREQ(args.GetArgumentAtIndex(0), "bar");
261 }
262 
TEST(ArgsTest,ReplaceArgumentAtIndexLonger)263 TEST(ArgsTest, ReplaceArgumentAtIndexLonger) {
264   Args args;
265   args.SetCommandString("foo ba b");
266   args.ReplaceArgumentAtIndex(0, "baar");
267   EXPECT_EQ(3u, args.GetArgumentCount());
268   EXPECT_STREQ(args.GetArgumentAtIndex(0), "baar");
269 }
270 
TEST(ArgsTest,ReplaceArgumentAtIndexOutOfRange)271 TEST(ArgsTest, ReplaceArgumentAtIndexOutOfRange) {
272   Args args;
273   args.SetCommandString("foo ba b");
274   args.ReplaceArgumentAtIndex(3, "baar");
275   EXPECT_EQ(3u, args.GetArgumentCount());
276   EXPECT_STREQ(args.GetArgumentAtIndex(2), "b");
277 }
278 
TEST(ArgsTest,ReplaceArgumentAtIndexFarOutOfRange)279 TEST(ArgsTest, ReplaceArgumentAtIndexFarOutOfRange) {
280   Args args;
281   args.SetCommandString("foo ba b");
282   args.ReplaceArgumentAtIndex(4, "baar");
283   EXPECT_EQ(3u, args.GetArgumentCount());
284   EXPECT_STREQ(args.GetArgumentAtIndex(2), "b");
285 }
286 
TEST(ArgsTest,Yaml)287 TEST(ArgsTest, Yaml) {
288   std::string buffer;
289   llvm::raw_string_ostream os(buffer);
290 
291   // Serialize.
292   Args args;
293   args.SetCommandString("this 'has' \"multiple\" args");
294   llvm::yaml::Output yout(os);
295   yout << args;
296   os.flush();
297 
298   llvm::outs() << buffer;
299 
300   // Deserialize.
301   Args deserialized;
302   llvm::yaml::Input yin(buffer);
303   yin >> deserialized;
304 
305   EXPECT_EQ(4u, deserialized.GetArgumentCount());
306   EXPECT_STREQ(deserialized.GetArgumentAtIndex(0), "this");
307   EXPECT_STREQ(deserialized.GetArgumentAtIndex(1), "has");
308   EXPECT_STREQ(deserialized.GetArgumentAtIndex(2), "multiple");
309   EXPECT_STREQ(deserialized.GetArgumentAtIndex(3), "args");
310 
311   llvm::ArrayRef<Args::ArgEntry> entries = deserialized.entries();
312   EXPECT_EQ(entries[0].GetQuoteChar(), '\0');
313   EXPECT_EQ(entries[1].GetQuoteChar(), '\'');
314   EXPECT_EQ(entries[2].GetQuoteChar(), '"');
315   EXPECT_EQ(entries[3].GetQuoteChar(), '\0');
316 }
317 
TEST(ArgsTest,GetShellSafeArgument)318 TEST(ArgsTest, GetShellSafeArgument) {
319   // Try escaping with bash at start/middle/end of the argument.
320   FileSpec bash("/bin/bash", FileSpec::Style::posix);
321   EXPECT_EQ(Args::GetShellSafeArgument(bash, "\"b"), "\\\"b");
322   EXPECT_EQ(Args::GetShellSafeArgument(bash, "a\""), "a\\\"");
323   EXPECT_EQ(Args::GetShellSafeArgument(bash, "a\"b"), "a\\\"b");
324 
325   FileSpec zsh("/bin/zsh", FileSpec::Style::posix);
326   EXPECT_EQ(Args::GetShellSafeArgument(zsh, R"('";()<>&|\)"),
327             R"(\'\"\;\(\)\<\>\&\|\\)");
328   // Normal characters and expressions that shouldn't be escaped.
329   EXPECT_EQ(Args::GetShellSafeArgument(zsh, "aA$1*"), "aA$1*");
330 
331   // String that doesn't need to be escaped
332   EXPECT_EQ(Args::GetShellSafeArgument(bash, "a"), "a");
333 
334   // Try escaping with tcsh and the tcsh-specific "$" escape.
335   FileSpec tcsh("/bin/tcsh", FileSpec::Style::posix);
336   EXPECT_EQ(Args::GetShellSafeArgument(tcsh, "a$b"), "a\\$b");
337   // Bash however doesn't need escaping for "$".
338   EXPECT_EQ(Args::GetShellSafeArgument(bash, "a$b"), "a$b");
339 
340   // Try escaping with an unknown shell.
341   FileSpec unknown_shell("/bin/unknown_shell", FileSpec::Style::posix);
342   EXPECT_EQ(Args::GetShellSafeArgument(unknown_shell, "a'b"), "a\\'b");
343   EXPECT_EQ(Args::GetShellSafeArgument(unknown_shell, "a\"b"), "a\\\"b");
344 }
345