1/* 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "cmd_parser.h" 17#include <algorithm> 18#include "resconfig_parser.h" 19#include "resource_util.h" 20#include "select_compile_parse.h" 21 22namespace OHOS { 23namespace Global { 24namespace Restool { 25using namespace std; 26const struct option PackageParser::CMD_OPTS[] = { 27 { "inputPath", required_argument, nullptr, Option::INPUTPATH }, 28 { "packageName", required_argument, nullptr, Option::PACKAGENAME }, 29 { "outputPath", required_argument, nullptr, Option::OUTPUTPATH }, 30 { "resHeader", required_argument, nullptr, Option::RESHEADER }, 31 { "forceWrite", no_argument, nullptr, Option::FORCEWRITE }, 32 { "version", no_argument, nullptr, Option::VERSION}, 33 { "modules", required_argument, nullptr, Option::MODULES }, 34 { "json", required_argument, nullptr, Option::JSON }, 35 { "startId", required_argument, nullptr, Option::STARTID }, 36 { "fileList", required_argument, nullptr, Option::FILELIST }, 37 { "append", required_argument, nullptr, Option::APPEND }, 38 { "combine", required_argument, nullptr, Option::COMBINE }, 39 { "dependEntry", required_argument, nullptr, Option::DEPENDENTRY }, 40 { "help", no_argument, nullptr, Option::HELP}, 41 { "ids", required_argument, nullptr, Option::IDS}, 42 { "defined-ids", required_argument, nullptr, Option::DEFINED_IDS}, 43 { "icon-check", no_argument, nullptr, Option::ICON_CHECK}, 44 { "target-config", required_argument, nullptr, Option::TARGET_CONFIG}, 45 { "defined-sysids", required_argument, nullptr, Option::DEFINED_SYSIDS}, 46 { "compressed-config", required_argument, nullptr, Option::COMPRESSED_CONFIG}, 47 { 0, 0, 0, 0}, 48}; 49 50const string PackageParser::CMD_PARAMS = ":i:p:o:r:m:j:e:l:x:fhvz"; 51 52uint32_t PackageParser::Parse(int argc, char *argv[]) 53{ 54 InitCommand(); 55 if (ParseCommand(argc, argv) != RESTOOL_SUCCESS) { 56 return RESTOOL_ERROR; 57 } 58 if (CheckParam() != RESTOOL_SUCCESS) { 59 return RESTOOL_ERROR; 60 } 61 AdaptResourcesDirForInput(); 62 return RESTOOL_SUCCESS; 63} 64 65const vector<string> &PackageParser::GetInputs() const 66{ 67 return inputs_; 68} 69 70const string &PackageParser::GetPackageName() const 71{ 72 return packageName_; 73} 74 75const string &PackageParser::GetOutput() const 76{ 77 return output_; 78} 79 80const vector<string> &PackageParser::GetResourceHeaders() const 81{ 82 return resourceHeaderPaths_; 83} 84 85bool PackageParser::GetForceWrite() const 86{ 87 return forceWrite_; 88} 89 90const vector<string> &PackageParser::GetModuleNames() const 91{ 92 return moduleNames_; 93} 94 95const string &PackageParser::GetConfig() const 96{ 97 return configPath_; 98} 99 100const string &PackageParser::GetRestoolPath() const 101{ 102 return restoolPath_; 103} 104 105uint32_t PackageParser::GetStartId() const 106{ 107 return startId_; 108} 109 110const string &PackageParser::GetDependEntry() const 111{ 112 return dependEntry_; 113} 114 115const vector<std::string> &PackageParser::GetSysIdDefinedPaths() const 116{ 117 return sysIdDefinedPaths_; 118} 119 120uint32_t PackageParser::AddInput(const string& argValue) 121{ 122 string inputPath = ResourceUtil::RealPath(argValue); 123 if (inputPath.empty()) { 124 cerr << "Error: invalid input '" << argValue << "'" << endl; 125 return RESTOOL_ERROR; 126 } 127 128 auto ret = find_if(inputs_.begin(), inputs_.end(), [inputPath](auto iter) {return inputPath == iter;}); 129 if (ret != inputs_.end()) { 130 cerr << "Error: repeat input '" << argValue << "'" << endl; 131 return RESTOOL_ERROR; 132 } 133 134 if (!IsAscii(inputPath)) { 135 return RESTOOL_ERROR; 136 } 137 inputs_.push_back(inputPath); 138 return RESTOOL_SUCCESS; 139} 140 141uint32_t PackageParser::AddSysIdDefined(const std::string& argValue) 142{ 143 string sysIdDefinedPath = ResourceUtil::RealPath(argValue); 144 if (sysIdDefinedPath.empty()) { 145 cerr << "Error: invalid system id_defined.json path: '" << argValue << "'" << endl; 146 return RESTOOL_ERROR; 147 } 148 149 auto ret = find_if(sysIdDefinedPaths_.begin(), sysIdDefinedPaths_.end(), 150 [sysIdDefinedPath](auto iter) {return sysIdDefinedPath == iter;}); 151 if (ret != sysIdDefinedPaths_.end()) { 152 cerr << "Error: repeat system id_defined.json path: '" << argValue << "'" << endl; 153 return RESTOOL_ERROR; 154 } 155 156 if (!IsAscii(sysIdDefinedPath)) { 157 return RESTOOL_ERROR; 158 } 159 sysIdDefinedPaths_.push_back(sysIdDefinedPath); 160 return RESTOOL_SUCCESS; 161} 162 163uint32_t PackageParser::AddPackageName(const string& argValue) 164{ 165 if (!packageName_.empty()) { 166 cerr << "Error: double package name " << packageName_ << " vs " << argValue << endl; 167 return RESTOOL_ERROR; 168 } 169 170 packageName_ = argValue; 171 return RESTOOL_SUCCESS; 172} 173 174uint32_t PackageParser::AddOutput(const string& argValue) 175{ 176 if (!output_.empty()) { 177 cerr << "Error: double output " << output_ << " vs " << argValue << endl; 178 return RESTOOL_ERROR; 179 } 180 181 output_ = ResourceUtil::RealPath(argValue); 182 if (output_.empty()) { 183 cerr << "Error: invalid output '" << argValue << "'" << endl; 184 return RESTOOL_ERROR; 185 } 186 if (!IsAscii(output_)) { 187 return RESTOOL_ERROR; 188 } 189 return RESTOOL_SUCCESS; 190} 191 192uint32_t PackageParser::AddResourceHeader(const string& argValue) 193{ 194 if (find(resourceHeaderPaths_.begin(), resourceHeaderPaths_.end(), argValue) != resourceHeaderPaths_.end()) { 195 cerr << "Error: '" << argValue << "' input duplicated." << endl; 196 return RESTOOL_ERROR; 197 } 198 resourceHeaderPaths_.push_back(argValue); 199 return RESTOOL_SUCCESS; 200} 201 202uint32_t PackageParser::ForceWrite() 203{ 204 forceWrite_ = true; 205 return RESTOOL_SUCCESS; 206} 207 208uint32_t PackageParser::PrintVersion() 209{ 210 cout << "Info: Restool version= " << RESTOOL_VERSION << endl; 211 exit(RESTOOL_SUCCESS); 212 return RESTOOL_SUCCESS; 213} 214 215uint32_t PackageParser::AddMoudleNames(const string& argValue) 216{ 217 if (!moduleNames_.empty()) { 218 cerr << "Error: -m double module name '" << argValue << "'" << endl; 219 return RESTOOL_ERROR; 220 } 221 222 ResourceUtil::Split(argValue, moduleNames_, ","); 223 for (auto it = moduleNames_.begin(); it != moduleNames_.end(); it++) { 224 auto ret = find_if(moduleNames_.begin(), moduleNames_.end(), [it](auto iter) {return *it == iter;}); 225 if (ret != it) { 226 cerr << "Error: double module name '" << *it << "'" << endl; 227 return RESTOOL_ERROR; 228 } 229 } 230 return RESTOOL_SUCCESS; 231} 232 233uint32_t PackageParser::AddConfig(const string& argValue) 234{ 235 if (!configPath_.empty()) { 236 cerr << "Error: double config.json " << configPath_ << " vs " << argValue << endl; 237 return RESTOOL_ERROR; 238 } 239 240 configPath_ = argValue; 241 return RESTOOL_SUCCESS; 242} 243 244uint32_t PackageParser::AddStartId(const string& argValue) 245{ 246 startId_ = strtoll(argValue.c_str(), nullptr, 16); // 16 is hexadecimal number 247 if ((startId_ >= 0x01000000 && startId_ < 0x06ffffff) || (startId_ >= 0x08000000 && startId_ < 0xffffffff)) { 248 return RESTOOL_SUCCESS; 249 } 250 cerr << "Error: invalid start id " << argValue << endl; 251 return RESTOOL_ERROR; 252} 253 254// -i input directory, add the resource directory 255void PackageParser::AdaptResourcesDirForInput() 256{ 257 if (!isFileList_ && !combine_) { // -l and increment compile -i, no need to add resource directory 258 for (auto &path : inputs_) { 259 path = FileEntry::FilePath(path).Append(RESOURCES_DIR).GetPath(); 260 } 261 } 262} 263 264uint32_t PackageParser::CheckParam() const 265{ 266 if (inputs_.empty() && append_.empty()) { 267 cerr << "Error: input path empty." << endl; 268 return RESTOOL_ERROR; 269 } 270 271 if (output_.empty()) { 272 cerr << "Error: output path empty." << endl; 273 return RESTOOL_ERROR; 274 } 275 276 if (isTtargetConfig_ && !append_.empty()) { 277 cerr << "Error: -x and --target-config cannot be used together." << endl; 278 return RESTOOL_ERROR; 279 } 280 281 if (!append_.empty()) { 282 return RESTOOL_SUCCESS; 283 } 284 285 if (packageName_.empty()) { 286 cerr << "Error: package name empty." << endl; 287 return RESTOOL_ERROR; 288 } 289 290 if (resourceHeaderPaths_.empty()) { 291 cerr << "Error: resource header path empty." << endl; 292 return RESTOOL_ERROR; 293 } 294 295 if (startId_ != 0 && !idDefinedInputPath_.empty()) { 296 cerr << "Error: set -e and --defined-ids cannot be used together." << endl; 297 return RESTOOL_ERROR; 298 } 299 300 return RESTOOL_SUCCESS; 301} 302 303bool PackageParser::IsFileList() const 304{ 305 return isFileList_; 306} 307 308uint32_t PackageParser::AddAppend(const string& argValue) 309{ 310 string appendPath = ResourceUtil::RealPath(argValue); 311 if (appendPath.empty()) { 312 cout << "Warning: invalid compress '" << argValue << "'" << endl; 313 appendPath = argValue; 314 } 315 auto ret = find_if(append_.begin(), append_.end(), [appendPath](auto iter) {return appendPath == iter;}); 316 if (ret != append_.end()) { 317 cerr << "Error: repeat input '" << argValue << "'" << endl; 318 return RESTOOL_ERROR; 319 } 320 if (!IsAscii(appendPath)) { 321 return RESTOOL_ERROR; 322 } 323 append_.push_back(appendPath); 324 return RESTOOL_SUCCESS; 325} 326 327const vector<string> &PackageParser::GetAppend() const 328{ 329 return append_; 330} 331 332uint32_t PackageParser::SetCombine() 333{ 334 combine_ = true; 335 return RESTOOL_SUCCESS; 336} 337 338bool PackageParser::GetCombine() const 339{ 340 return combine_; 341} 342 343uint32_t PackageParser::AddDependEntry(const string& argValue) 344{ 345 dependEntry_ = argValue; 346 return RESTOOL_SUCCESS; 347} 348 349uint32_t PackageParser::ShowHelp() const 350{ 351 auto &parser = CmdParser<PackageParser>::GetInstance(); 352 parser.ShowUseage(); 353 exit(RESTOOL_SUCCESS); 354 return RESTOOL_SUCCESS; 355} 356 357uint32_t PackageParser::SetIdDefinedOutput(const string& argValue) 358{ 359 idDefinedOutput_ = argValue; 360 return RESTOOL_SUCCESS; 361} 362 363const string &PackageParser::GetIdDefinedOutput() const 364{ 365 return idDefinedOutput_; 366} 367 368uint32_t PackageParser::SetIdDefinedInputPath(const string& argValue) 369{ 370 idDefinedInputPath_ = argValue; 371 return RESTOOL_SUCCESS; 372} 373 374const string &PackageParser::GetIdDefinedInputPath() const 375{ 376 return idDefinedInputPath_; 377} 378 379uint32_t PackageParser::IconCheck() 380{ 381 isIconCheck_ = true; 382 return RESTOOL_SUCCESS; 383} 384 385bool PackageParser::GetIconCheck() const 386{ 387 return isIconCheck_; 388} 389 390uint32_t PackageParser::ParseTargetConfig(const string& argValue) 391{ 392 if (isTtargetConfig_) { 393 cerr << "Error: repeat input '--target-config'" << endl; 394 return RESTOOL_ERROR; 395 } 396 if (!SelectCompileParse::ParseTargetConfig(argValue, targetConfig_)) { 397 cerr << "Error: '" << argValue << "' is not valid parameter." << endl; 398 return RESTOOL_ERROR; 399 } 400 isTtargetConfig_ = true; 401 return RESTOOL_SUCCESS; 402} 403 404const TargetConfig &PackageParser::GetTargetConfigValues() const 405{ 406 return targetConfig_; 407} 408 409bool PackageParser::IsTargetConfig() const 410{ 411 return isTtargetConfig_; 412} 413 414bool PackageParser::IsAscii(const string& argValue) const 415{ 416#ifdef __WIN32 417 auto result = find_if(argValue.begin(), argValue.end(), [](auto iter) { 418 if ((iter & 0x80) != 0) { 419 return true; 420 } 421 return false; 422 }); 423 if (result != argValue.end()) { 424 cerr << "Error: '" << argValue << "' must be ASCII" << endl; 425 return false; 426 } 427#endif 428 return true; 429} 430 431uint32_t PackageParser::AddCompressionPath(const std::string& argValue) 432{ 433 if (!compressionPath_.empty()) { 434 cerr << "Error: double opt-compression.json " << compressionPath_ << " vs " << argValue << endl; 435 return RESTOOL_ERROR; 436 } 437 compressionPath_ = argValue; 438 return RESTOOL_SUCCESS; 439} 440 441const std::string &PackageParser::GetCompressionPath() const 442{ 443 return compressionPath_; 444} 445 446void PackageParser::InitCommand() 447{ 448 using namespace placeholders; 449 handles_.emplace(Option::INPUTPATH, bind(&PackageParser::AddInput, this, _1)); 450 handles_.emplace(Option::PACKAGENAME, bind(&PackageParser::AddPackageName, this, _1)); 451 handles_.emplace(Option::OUTPUTPATH, bind(&PackageParser::AddOutput, this, _1)); 452 handles_.emplace(Option::RESHEADER, bind(&PackageParser::AddResourceHeader, this, _1)); 453 handles_.emplace(Option::FORCEWRITE, [this](const string &) -> uint32_t { return ForceWrite(); }); 454 handles_.emplace(Option::VERSION, [this](const string &) -> uint32_t { return PrintVersion(); }); 455 handles_.emplace(Option::MODULES, bind(&PackageParser::AddMoudleNames, this, _1)); 456 handles_.emplace(Option::JSON, bind(&PackageParser::AddConfig, this, _1)); 457 handles_.emplace(Option::STARTID, bind(&PackageParser::AddStartId, this, _1)); 458 handles_.emplace(Option::APPEND, bind(&PackageParser::AddAppend, this, _1)); 459 handles_.emplace(Option::COMBINE, [this](const string &) -> uint32_t { return SetCombine(); }); 460 handles_.emplace(Option::DEPENDENTRY, bind(&PackageParser::AddDependEntry, this, _1)); 461 handles_.emplace(Option::HELP, [this](const string &) -> uint32_t { return ShowHelp(); }); 462 handles_.emplace(Option::IDS, bind(&PackageParser::SetIdDefinedOutput, this, _1)); 463 handles_.emplace(Option::DEFINED_IDS, bind(&PackageParser::SetIdDefinedInputPath, this, _1)); 464 handles_.emplace(Option::ICON_CHECK, [this](const string &) -> uint32_t { return IconCheck(); }); 465 handles_.emplace(Option::TARGET_CONFIG, bind(&PackageParser::ParseTargetConfig, this, _1)); 466 handles_.emplace(Option::DEFINED_SYSIDS, bind(&PackageParser::AddSysIdDefined, this, _1)); 467 handles_.emplace(Option::COMPRESSED_CONFIG, bind(&PackageParser::AddCompressionPath, this, _1)); 468} 469 470uint32_t PackageParser::HandleProcess(int c, const string& argValue) 471{ 472 auto handler = handles_.find(c); 473 if (handler == handles_.end()) { 474 cerr << "Error: unsupport " << c << endl; 475 return RESTOOL_ERROR; 476 } 477 return handler->second(argValue); 478} 479 480uint32_t PackageParser::ParseFileList(const string& fileListPath) 481{ 482 isFileList_ = true; 483 ResConfigParser resConfigParser; 484 if (resConfigParser.Init(fileListPath, [this](int c, const string &argValue) -> uint32_t { 485 return HandleProcess(c, argValue); 486 }) != RESTOOL_SUCCESS) { 487 return RESTOOL_ERROR; 488 } 489 return RESTOOL_SUCCESS; 490} 491 492uint32_t PackageParser::CheckError(int argc, char *argv[], int c, int optIndex) 493{ 494 if (optIndex != -1) { 495 if ((optarg == nullptr && (optind - 1 < 0 || optind - 1 >= argc)) || 496 (optarg != nullptr && (optind - 2 < 0 || optind - 2 >= argc))) { // 1 or 2 menas optind offset value 497 return RESTOOL_ERROR; 498 } 499 string curOpt = (optarg == nullptr) ? argv[optind - 1] : argv[optind - 2]; 500 if (curOpt != ("--" + string(CMD_OPTS[optIndex].name))) { 501 cerr << "Error: unknown option " << curOpt << endl; 502 return RESTOOL_ERROR; 503 } 504 } 505 if (c == Option::UNKNOWN) { 506 if (optopt == 0 && (optind - 1 < 0 || optind - 1 >= argc)) { 507 return RESTOOL_ERROR; 508 } 509 string optUnknown = (optopt == 0) ? argv[optind - 1] : ("-" + string(1, optopt)); 510 cerr << "Error: unknown option " << optUnknown << endl; 511 return RESTOOL_ERROR; 512 } 513 if (c == Option::NO_ARGUMENT) { 514 if (optind - 1 < 0 || optind - 1 >= argc) { 515 return RESTOOL_ERROR; 516 } 517 if (IsLongOpt(argc, argv)) { 518 cerr << "Error: option " << argv[optind - 1] << " must have argument" << endl; 519 } else { 520 cerr << "Error: unknown option " << argv[optind - 1] << endl; 521 } 522 return RESTOOL_ERROR; 523 } 524 return RESTOOL_SUCCESS; 525} 526 527uint32_t PackageParser::ParseCommand(int argc, char *argv[]) 528{ 529 restoolPath_ = string(argv[0]); 530 while (true) { 531 int optIndex = -1; 532 int c = getopt_long(argc, argv, CMD_PARAMS.c_str(), CMD_OPTS, &optIndex); 533 if (CheckError(argc, argv, c, optIndex) != RESTOOL_SUCCESS) { 534 return RESTOOL_ERROR; 535 } 536 if (c == Option::END) { 537 if (argc == optind) { 538 break; 539 } 540 string errmsg = "Error: invalid arguments : "; 541 for (int i = optind; i < argc; i++) { 542 errmsg.append(argv[i]).append(" "); 543 } 544 cerr << errmsg << endl; 545 return RESTOOL_ERROR; 546 } 547 548 string argValue = (optarg != nullptr) ? optarg : ""; 549 if (c == Option::FILELIST) { 550 return ParseFileList(argValue); 551 } 552 if (HandleProcess(c, argValue) != RESTOOL_SUCCESS) { 553 return RESTOOL_ERROR; 554 } 555 } 556 return RESTOOL_SUCCESS; 557} 558 559bool PackageParser::IsLongOpt(int argc, char *argv[]) const 560{ 561 if (optind - 1 < 0 || optind - 1 >= argc) { 562 return false; 563 } 564 for (auto iter : CMD_OPTS) { 565 if (optopt == iter.val && argv[optind - 1] == ("--" + string(iter.name))) { 566 return true; 567 } 568 } 569 return false; 570} 571} 572} 573} 574