1 /* 2 * Copyright (C) 2018 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 "Command.h" 18 19 #include <iomanip> 20 #include <iostream> 21 #include <string> 22 #include <vector> 23 24 #include "android-base/stringprintf.h" 25 #include "android-base/utf8.h" 26 #include "androidfw/StringPiece.h" 27 28 #include "trace/TraceBuffer.h" 29 #include "util/Util.h" 30 31 using android::base::StringPrintf; 32 using android::StringPiece; 33 34 namespace aapt { 35 36 std::string GetSafePath(const StringPiece& arg) { 37 #ifdef _WIN32 38 // If the path exceeds the maximum path length for Windows, encode the path using the 39 // extended-length prefix 40 std::wstring path16; 41 CHECK(android::base::UTF8PathToWindowsLongPath(arg.data(), &path16)) 42 << "Failed to convert file path to UTF-16: file path " << arg.data(); 43 44 std::string path8; 45 CHECK(android::base::WideToUTF8(path16, &path8)) 46 << "Failed to convert file path back to UTF-8: file path " << arg.data(); 47 48 return path8; 49 #else 50 return arg.to_string(); 51 #endif 52 } 53 54 void Command::AddRequiredFlag(const StringPiece& name, const StringPiece& description, 55 std::string* value, uint32_t flags) { 56 auto func = [value, flags](const StringPiece& arg) -> bool { 57 *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string(); 58 return true; 59 }; 60 61 flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func)); 62 } 63 64 void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& description, 65 std::vector<std::string>* value, uint32_t flags) { 66 auto func = [value, flags](const StringPiece& arg) -> bool { 67 value->push_back((flags & Command::kPath) ? GetSafePath(arg) : arg.to_string()); 68 return true; 69 }; 70 71 flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func)); 72 } 73 74 void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description, 75 Maybe<std::string>* value, uint32_t flags) { 76 auto func = [value, flags](const StringPiece& arg) -> bool { 77 *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string(); 78 return true; 79 }; 80 81 flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func)); 82 } 83 84 void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description, 85 std::vector<std::string>* value, uint32_t flags) { 86 auto func = [value, flags](const StringPiece& arg) -> bool { 87 value->push_back((flags & Command::kPath) ? GetSafePath(arg) : arg.to_string()); 88 return true; 89 }; 90 91 flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func)); 92 } 93 94 void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description, 95 std::unordered_set<std::string>* value) { 96 auto func = [value](const StringPiece& arg) -> bool { 97 value->insert(arg.to_string()); 98 return true; 99 }; 100 101 flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func)); 102 } 103 104 void Command::AddOptionalSwitch(const StringPiece& name, const StringPiece& description, 105 bool* value) { 106 auto func = [value](const StringPiece& arg) -> bool { 107 *value = true; 108 return true; 109 }; 110 111 flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 0, func)); 112 } 113 114 void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) { 115 subcommand->full_subcommand_name_ = StringPrintf("%s %s", name_.data(), subcommand->name_.data()); 116 if (experimental) { 117 experimental_subcommands_.push_back(std::move(subcommand)); 118 } else { 119 subcommands_.push_back(std::move(subcommand)); 120 } 121 } 122 123 void Command::SetDescription(const StringPiece& description) { 124 description_ = description.to_string(); 125 } 126 127 void Command::Usage(std::ostream* out) { 128 constexpr size_t kWidth = 50; 129 130 *out << full_subcommand_name_; 131 132 if (!subcommands_.empty()) { 133 *out << " [subcommand]"; 134 } 135 136 *out << " [options]"; 137 for (const Flag& flag : flags_) { 138 if (flag.is_required) { 139 *out << " " << flag.name << " arg"; 140 } 141 } 142 143 *out << " files...\n"; 144 145 if (!subcommands_.empty()) { 146 *out << "\nSubcommands:\n"; 147 for (auto& subcommand : subcommands_) { 148 std::string argline = subcommand->name_; 149 150 // Split the description by newlines and write out the argument (which is 151 // empty after the first line) followed by the description line. This will make sure 152 // that multiline descriptions are still right justified and aligned. 153 for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) { 154 *out << " " << std::setw(kWidth) << std::left << argline << line << "\n"; 155 argline = " "; 156 } 157 } 158 } 159 160 *out << "\nOptions:\n"; 161 162 for (const Flag& flag : flags_) { 163 std::string argline = flag.name; 164 if (flag.num_args > 0) { 165 argline += " arg"; 166 } 167 168 // Split the description by newlines and write out the argument (which is 169 // empty after the first line) followed by the description line. This will make sure 170 // that multiline descriptions are still right justified and aligned. 171 for (StringPiece line : util::Tokenize(flag.description, '\n')) { 172 *out << " " << std::setw(kWidth) << std::left << argline << line << "\n"; 173 argline = " "; 174 } 175 } 176 *out << " " << std::setw(kWidth) << std::left << "-h" 177 << "Displays this help menu\n"; 178 out->flush(); 179 } 180 181 int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_error) { 182 TRACE_NAME_ARGS("Command::Execute", args); 183 std::vector<std::string> file_args; 184 185 for (size_t i = 0; i < args.size(); i++) { 186 const StringPiece& arg = args[i]; 187 if (*(arg.data()) != '-') { 188 // Continue parsing as the subcommand if the first argument matches one of the subcommands 189 if (i == 0) { 190 for (auto& subcommand : subcommands_) { 191 if (arg == subcommand->name_ || (!subcommand->short_name_.empty() 192 && arg == subcommand->short_name_)) { 193 return subcommand->Execute( 194 std::vector<StringPiece>(args.begin() + 1, args.end()), out_error); 195 } 196 } 197 for (auto& subcommand : experimental_subcommands_) { 198 if (arg == subcommand->name_ || (!subcommand->short_name_.empty() 199 && arg == subcommand->short_name_)) { 200 return subcommand->Execute( 201 std::vector<StringPiece>(args.begin() + 1, args.end()), out_error); 202 } 203 } 204 } 205 206 file_args.push_back(GetSafePath(arg)); 207 continue; 208 } 209 210 if (arg == "-h" || arg == "--help") { 211 Usage(out_error); 212 return 1; 213 } 214 215 bool match = false; 216 for (Flag& flag : flags_) { 217 if (arg == flag.name) { 218 if (flag.num_args > 0) { 219 i++; 220 if (i >= args.size()) { 221 *out_error << flag.name << " missing argument.\n\n"; 222 Usage(out_error); 223 return false; 224 } 225 flag.action(args[i]); 226 } else { 227 flag.action({}); 228 } 229 flag.found = true; 230 match = true; 231 break; 232 } 233 } 234 235 if (!match) { 236 *out_error << "unknown option '" << arg << "'.\n\n"; 237 Usage(out_error); 238 return 1; 239 } 240 } 241 242 for (const Flag& flag : flags_) { 243 if (flag.is_required && !flag.found) { 244 *out_error << "missing required flag " << flag.name << "\n\n"; 245 Usage(out_error); 246 return 1; 247 } 248 } 249 250 return Action(file_args); 251 } 252 253 } // namespace aapt 254