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
22 namespace OHOS {
23 namespace Global {
24 namespace Restool {
25 using namespace std;
26 const 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
50 const string PackageParser::CMD_PARAMS = ":i:p:o:r:m:j:e:l:x:fhvz";
51
Parse(int argc, char *argv[])52 uint32_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
GetInputs() const65 const vector<string> &PackageParser::GetInputs() const
66 {
67 return inputs_;
68 }
69
GetPackageName() const70 const string &PackageParser::GetPackageName() const
71 {
72 return packageName_;
73 }
74
GetOutput() const75 const string &PackageParser::GetOutput() const
76 {
77 return output_;
78 }
79
GetResourceHeaders() const80 const vector<string> &PackageParser::GetResourceHeaders() const
81 {
82 return resourceHeaderPaths_;
83 }
84
GetForceWrite() const85 bool PackageParser::GetForceWrite() const
86 {
87 return forceWrite_;
88 }
89
GetModuleNames() const90 const vector<string> &PackageParser::GetModuleNames() const
91 {
92 return moduleNames_;
93 }
94
GetConfig() const95 const string &PackageParser::GetConfig() const
96 {
97 return configPath_;
98 }
99
GetRestoolPath() const100 const string &PackageParser::GetRestoolPath() const
101 {
102 return restoolPath_;
103 }
104
GetStartId() const105 uint32_t PackageParser::GetStartId() const
106 {
107 return startId_;
108 }
109
GetDependEntry() const110 const string &PackageParser::GetDependEntry() const
111 {
112 return dependEntry_;
113 }
114
GetSysIdDefinedPaths() const115 const vector<std::string> &PackageParser::GetSysIdDefinedPaths() const
116 {
117 return sysIdDefinedPaths_;
118 }
119
AddInput(const string& argValue)120 uint32_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
AddSysIdDefined(const std::string& argValue)141 uint32_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
AddPackageName(const string& argValue)163 uint32_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
AddOutput(const string& argValue)174 uint32_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
AddResourceHeader(const string& argValue)192 uint32_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
ForceWrite()202 uint32_t PackageParser::ForceWrite()
203 {
204 forceWrite_ = true;
205 return RESTOOL_SUCCESS;
206 }
207
PrintVersion()208 uint32_t PackageParser::PrintVersion()
209 {
210 cout << "Info: Restool version= " << RESTOOL_VERSION << endl;
211 exit(RESTOOL_SUCCESS);
212 return RESTOOL_SUCCESS;
213 }
214
AddMoudleNames(const string& argValue)215 uint32_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
AddConfig(const string& argValue)233 uint32_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
AddStartId(const string& argValue)244 uint32_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
AdaptResourcesDirForInput()255 void 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
CheckParam() const264 uint32_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
IsFileList() const303 bool PackageParser::IsFileList() const
304 {
305 return isFileList_;
306 }
307
AddAppend(const string& argValue)308 uint32_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
GetAppend() const327 const vector<string> &PackageParser::GetAppend() const
328 {
329 return append_;
330 }
331
SetCombine()332 uint32_t PackageParser::SetCombine()
333 {
334 combine_ = true;
335 return RESTOOL_SUCCESS;
336 }
337
GetCombine() const338 bool PackageParser::GetCombine() const
339 {
340 return combine_;
341 }
342
AddDependEntry(const string& argValue)343 uint32_t PackageParser::AddDependEntry(const string& argValue)
344 {
345 dependEntry_ = argValue;
346 return RESTOOL_SUCCESS;
347 }
348
ShowHelp() const349 uint32_t PackageParser::ShowHelp() const
350 {
351 auto &parser = CmdParser<PackageParser>::GetInstance();
352 parser.ShowUseage();
353 exit(RESTOOL_SUCCESS);
354 return RESTOOL_SUCCESS;
355 }
356
SetIdDefinedOutput(const string& argValue)357 uint32_t PackageParser::SetIdDefinedOutput(const string& argValue)
358 {
359 idDefinedOutput_ = argValue;
360 return RESTOOL_SUCCESS;
361 }
362
GetIdDefinedOutput() const363 const string &PackageParser::GetIdDefinedOutput() const
364 {
365 return idDefinedOutput_;
366 }
367
SetIdDefinedInputPath(const string& argValue)368 uint32_t PackageParser::SetIdDefinedInputPath(const string& argValue)
369 {
370 idDefinedInputPath_ = argValue;
371 return RESTOOL_SUCCESS;
372 }
373
GetIdDefinedInputPath() const374 const string &PackageParser::GetIdDefinedInputPath() const
375 {
376 return idDefinedInputPath_;
377 }
378
IconCheck()379 uint32_t PackageParser::IconCheck()
380 {
381 isIconCheck_ = true;
382 return RESTOOL_SUCCESS;
383 }
384
GetIconCheck() const385 bool PackageParser::GetIconCheck() const
386 {
387 return isIconCheck_;
388 }
389
ParseTargetConfig(const string& argValue)390 uint32_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
GetTargetConfigValues() const404 const TargetConfig &PackageParser::GetTargetConfigValues() const
405 {
406 return targetConfig_;
407 }
408
IsTargetConfig() const409 bool PackageParser::IsTargetConfig() const
410 {
411 return isTtargetConfig_;
412 }
413
IsAscii(const string& argValue) const414 bool 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
AddCompressionPath(const std::string& argValue)431 uint32_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
GetCompressionPath() const441 const std::string &PackageParser::GetCompressionPath() const
442 {
443 return compressionPath_;
444 }
445
InitCommand()446 void 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
HandleProcess(int c, const string& argValue)470 uint32_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
ParseFileList(const string& fileListPath)480 uint32_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
CheckError(int argc, char *argv[], int c, int optIndex)492 uint32_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
ParseCommand(int argc, char *argv[])527 uint32_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
IsLongOpt(int argc, char *argv[]) const559 bool 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