1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/commands/cvd/selector/arguments_separator.h"
18
19 #include <deque>
20
21 #include <android-base/strings.h>
22
23 #include "common/libs/utils/contains.h"
24
25 namespace cuttlefish {
26 namespace selector {
27
Parse(const FlagsRegistration & flag_registration,const std::vector<std::string> & input_args)28 Result<std::unique_ptr<ArgumentsSeparator>> ArgumentsSeparator::Parse(
29 const FlagsRegistration& flag_registration,
30 const std::vector<std::string>& input_args) {
31 LexerFlagsSpecification lexer_flag_spec{
32 .known_boolean_flags = flag_registration.known_boolean_flags,
33 .known_value_flags = flag_registration.known_value_flags,
34 };
35 auto lexer = CF_EXPECT(ArgumentsLexerBuilder::Build(lexer_flag_spec));
36 CF_EXPECT(lexer != nullptr);
37 ArgumentsSeparator* new_arg_separator =
38 new ArgumentsSeparator(std::move(lexer), input_args, flag_registration);
39 CF_EXPECT(new_arg_separator != nullptr,
40 "Memory allocation failed for ArgumentSeparator");
41 std::unique_ptr<ArgumentsSeparator> arg_separator{new_arg_separator};
42 CF_EXPECT(arg_separator->Parse());
43 return std::move(arg_separator);
44 }
45
Parse(const FlagsRegistration & flag_registration,const CvdProtobufArg & input_args)46 Result<std::unique_ptr<ArgumentsSeparator>> ArgumentsSeparator::Parse(
47 const FlagsRegistration& flag_registration,
48 const CvdProtobufArg& input_args) {
49 std::vector<std::string> input_args_vec;
50 input_args_vec.reserve(input_args.size());
51 for (const auto& input_arg : input_args) {
52 input_args_vec.emplace_back(input_arg);
53 }
54 auto arg_separator = CF_EXPECT(Parse(flag_registration, input_args_vec));
55 return std::move(arg_separator);
56 }
57
Parse(const FlagsRegistration & flag_registration,const std::string & input_args,const std::string delim)58 Result<std::unique_ptr<ArgumentsSeparator>> ArgumentsSeparator::Parse(
59 const FlagsRegistration& flag_registration, const std::string& input_args,
60 const std::string delim) {
61 std::vector<std::string> input_args_vec =
62 android::base::Tokenize(input_args, delim);
63 auto arg_separator = CF_EXPECT(Parse(flag_registration, input_args_vec));
64 return std::move(arg_separator);
65 }
66
ArgumentsSeparator(std::unique_ptr<ArgumentsLexer> && lexer,const std::vector<std::string> & input_args,const FlagsRegistration & flag_registration)67 ArgumentsSeparator::ArgumentsSeparator(
68 std::unique_ptr<ArgumentsLexer>&& lexer,
69 const std::vector<std::string>& input_args,
70 const FlagsRegistration& flag_registration)
71 : lexer_(std::move(lexer)),
72 input_args_(input_args),
73 known_boolean_flags_(flag_registration.known_boolean_flags),
74 known_value_flags_(flag_registration.known_value_flags),
75 valid_subcmds_(flag_registration.valid_subcommands) {}
76
Parse()77 Result<void> ArgumentsSeparator::Parse() {
78 auto output = CF_EXPECT(ParseInternal());
79 prog_path_ = std::move(output.prog_path);
80 cvd_args_ = std::move(output.cvd_args);
81 sub_cmd_ = std::move(output.sub_cmd);
82 sub_cmd_args_ = std::move(output.sub_cmd_args);
83 return {};
84 }
85
86 /*
87 * prog_name, <optional cvd flags>, sub_cmd, <optional sub_cmd flags>
88 *
89 * -- could be included, which makes things complicated. However, if -- is
90 * part of cvd flags, it's ill-formatted. If -- is among sub_cmd flags,
91 * we will just forward it.
92 *
93 * If something like this is really needed, use the suggested alternative:
94 * original: cvd --some_flag -- --this-is-value start --subcmd_args
95 * alternative: cvd --some_flag="--this-is-value" start --subcmd_args
96 *
97 */
ParseInternal()98 Result<ArgumentsSeparator::Output> ArgumentsSeparator::ParseInternal() {
99 CF_EXPECT(lexer_ != nullptr);
100 CF_EXPECT(!input_args_.empty());
101 Output output;
102
103 auto tokenized = CF_EXPECT(lexer_->Tokenize(input_args_));
104 std::deque<ArgToken> tokens_queue{tokenized.begin(), tokenized.end()};
105
106 // take program path/name
107 CF_EXPECT(!tokens_queue.empty() &&
108 tokens_queue.front().Type() == ArgType::kPositional);
109 output.prog_path = std::move(tokens_queue.front().Token());
110 tokens_queue.pop_front();
111
112 // break loop either if there is no token or
113 // the subcommand token is consumed
114 bool cvd_flags_mode = true;
115 while (!tokens_queue.empty() && cvd_flags_mode) {
116 const auto current = std::move(tokens_queue.front());
117 const auto current_type = current.Type();
118 const auto& current_token = current.Token();
119 tokens_queue.pop_front();
120
121 // look up next if any
122 std::optional<ArgToken> next;
123 if (!tokens_queue.empty()) {
124 next = tokens_queue.front();
125 }
126
127 switch (current_type) {
128 case ArgType::kKnownValueFlag: {
129 output.cvd_args.emplace_back(current_token);
130 if (next && next->Type() == ArgType::kPositional) {
131 output.cvd_args.emplace_back(next->Token());
132 tokens_queue.pop_front();
133 }
134 } break;
135 case ArgType::kKnownFlagAndValue:
136 case ArgType::kKnownBoolFlag:
137 case ArgType::kKnownBoolNoFlag: {
138 output.cvd_args.emplace_back(current_token);
139 } break;
140 case ArgType::kPositional: {
141 output.sub_cmd = current.Token();
142 CF_EXPECT(output.sub_cmd != std::nullopt);
143 CF_EXPECT(Contains(valid_subcmds_, output.sub_cmd),
144 "Subcommand " << *(output.sub_cmd) << " is not valid");
145 cvd_flags_mode = false;
146 } break;
147 case ArgType::kDoubleDash: {
148 return CF_ERR("--"
149 << " is not allowed within cvd specific flags.");
150 }
151 case ArgType::kUnknownFlag:
152 case ArgType::kError: {
153 return CF_ERR(current.Token()
154 << " in cvd-specific flags is disallowed.");
155 }
156 }
157 }
158 while (!tokens_queue.empty()) {
159 auto token = std::move(tokens_queue.front().Token());
160 output.sub_cmd_args.emplace_back(std::move(token));
161 tokens_queue.pop_front();
162 }
163 return output;
164 }
165
166 } // namespace selector
167 } // namespace cuttlefish
168