• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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