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