!19167 merge master0501 into master

hassubcommand 与sdk保持一致

Created-by: dsz2025
Commit-by: duansizhao
Merged-by: openharmony_ci
Description: **IssueNo**:https://gitcode.com/openharmony/build/issues/4508

**Description**:

**稳定性自检:**
| 自检项                                                       | 自检结果  |
| ------------------------------------------------------------ | -------- |
| 涉及跨进程调用的相关操作需要抛至主线程或加锁防止并发              |          |
| 成员变量进行赋值或创建需要排查并发                               |          |
| 谨慎在lambda表达式中使用引用捕获                                |          |
| 谨慎在未经拷贝的情况下使用外部传入的string、C字符串               |          |
| map\vector\list\set等stl模板类使用时需要排查并发                |          |
| 谨慎考虑加锁范围                                               |          |
| 在IPC通信中谨慎使用同步通信方式                                 |          |
| 禁止传递this指针至其他模块或线程(特别是eventhandler任务)        |          |
| 禁止将外部传入的裸指针在内部直接构造智能指针                      |          |
| 禁止多个独立创建的智能指针管理同一地址                           |          |
| 禁止在析构函数中抛异步任务                                      |          |
| 禁止js对象在非js线程(例如在IPC线程)创建、使用或销毁             |          |
| 禁止在对外接口中未经判空直接使用外部传入的指针                    |          |
| 禁止接口返回局部变量引用                                        |          |
| 禁止在信号函数中加锁                                            |          |
| 禁止在关键流程(SA启动、应用启动等主流程)执行耗时的操作           |          |
| 禁止将同一个cpp编译在不同的so中                                 |          |

**安全编码自检:**
| 自检项                                                          | 自检结果 |
| -------------------------------------------------------------- | -------- |
| 裸指针避免通过隐式转换构造为sptr                                 |          |
| json对象在取值之前必须先判断类型,避免类型不匹配                   |          |
| 序列化时必须对传入的数组大小进行校验,避免出现超大数组              |          |
| 避免使用未明确位宽的整型,选择使用int8_t、uint8_t等类型            |          |
| 外部传入的路径要做规范化校验,对路径中的.、..、../等特殊字符严格校验 |          |
| 指针变量、表示资源描述符的变量、bool变量必须赋初值                  |          |
| readParcelable获取的对象使用前需要判空                            |          |
| 分配和释放内存的函数需要成对出现                                   |          |
| 申请内存后异常退出前需要及时进行内存释放                            |          |
| 内存申请前必须对内存大小进行合法性校验                              |          |
| 内存分配后必须判断是否成功                                         |          |
| 禁止使用realloc、alloca函数                                       |          |
| 禁止打印文件路径、口令等敏感信息,如有需要,使用private修饰          |          |
| 禁止打印内存地址                                                  |          |
| 整数之间运算时必须严格检查,确保不会出现溢出、反转、除0               |          |
| 禁止对有符号整数进行位操作符运算                                    |          |
| 禁止对指针进行逻辑或位运算                                         |          |
| 循环次数如果收外部数据控制,需要检验其合法性                         |          |
| 禁止使用内存操作类危险函数,需要使用安全函数                         |          |
| 谨慎使用不可重入函数                                               |          |
| 必须检查安全函数的返回值,并进行正确处理                             |          |
| 禁止仅通过TokenType类型判断绕过权限校验                             |          |

**TDD Result**:

**XTS Result**:

### 是否已执行L0用例
- [ ] 已验证
- [ ] 不涉及。如不涉及,请写明理由


See merge request: openharmony/ability_ability_runtime!19167
This commit is contained in:
openharmony_ci
2026-05-01 22:02:17 +08:00
6 changed files with 398 additions and 283 deletions
+3 -8
View File
@@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "Example CLI tool, demonstrates CLI tool specification implementation (with subcommand format)",
"executablePath": "/system/bin/cli_tool/executable/ohos-example",
"hasSubcommands": true,
"hasSubCommand": true,
"requirePermissions": [],
"inputSchema": {
"type": "object",
@@ -56,7 +56,7 @@
"description": "Current status"
}
},
"required": ["percentage", "status"]
"required": ["type", "percentage", "status"]
}
}
},
@@ -65,12 +65,7 @@
"requirePermissions": [],
"inputSchema": {
"type": "object",
"properties": {
"reserved": {
"type": "string",
"description": "Reserved placeholder parameter for future use, not required for normal calls"
}
}
"properties": {}
},
"outputSchema": {
"type": "object",
+131 -84
View File
@@ -14,120 +14,167 @@
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstring>
#include <ctime>
// Constants for magic numbers
namespace {
constexpr int PROGRESS_MAX = 100;
constexpr int MIN_ARGC = 2;
constexpr int PROGRESS_MAX = 100;
constexpr int MIN_ARGC = 2;
constexpr const char* VERSION = "1.0.0";
constexpr const char* BUILD_TIME = "2026-04-04 00:00:00";
}
std::string EscapeJson(const std::string& input)
{
std::string escaped;
escaped.reserve(input.size());
for (char ch : input) {
switch (ch) {
case '\\':
escaped += "\\\\";
break;
case '"':
escaped += "\\\"";
break;
case '\n':
escaped += "\\n";
break;
case '\r':
escaped += "\\r";
break;
case '\t':
escaped += "\\t";
break;
default:
escaped += ch;
break;
}
}
return escaped;
}
void EmitProgress(int percentage, const std::string& status)
{
std::cout << "{\"type\": \"progress\", "
<< "\"percentage\": " << percentage << ", "
<< "\"status\": \"" << status << "\""
<< "}" << std::endl;
std::cout << "{\"type\":\"progress\",\"percentage\":" << percentage
<< ",\"status\":\"" << EscapeJson(status) << "\"}" << std::endl;
}
void EmitRunResult(const std::string& result)
void EmitSuccessResult(const std::string& dataJson)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"success\", "
<< "\"data\": {"
<< "\"result\": \"" << result << "\""
<< "}}"
<< "}" << std::endl;
}
void EmitVersionResult(const std::string& version, const std::string& buildTime)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"success\", "
<< "\"data\": {"
<< "\"version\": \"" << version << "\", "
<< "\"build_time\": \"" << buildTime << "\""
<< "}}"
<< "}" << std::endl;
std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":"
<< dataJson << "}" << std::endl;
}
void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"failed\", "
<< "\"errCode\": \"" << errCode << "\", "
<< "\"errMsg\": \"" << errMsg << "\", "
<< "\"suggestion\": \"" << suggestion << "\""
<< "}" << std::endl;
}
int RunCommand(const std::vector<std::string>& args)
{
EmitProgress(0, "starting");
std::string result = "执行完成";
for (size_t i = 0; i < args.size(); i++) {
if (i > 0 || !result.empty()) {
result += " ";
}
result += args[i];
int progress = static_cast<int>((i + 1) * PROGRESS_MAX / (args.size() + 1));
EmitProgress(progress, "running");
}
EmitProgress(PROGRESS_MAX, "completed");
EmitRunResult(result);
return 0;
}
int VersionCommand()
{
std::string version = "1.0.0";
const char* buildTime = "2026-04-04 00:00:00";
EmitVersionResult(version, buildTime);
return 0;
std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\""
<< EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg)
<< "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl;
}
void ShowHelp()
{
std::cout << "Usage: ohos-example <subcommand> [args]" << std::endl;
std::cout << "Usage: ohos-example <subcommand> [options]" << std::endl;
std::cout << "Subcommands:" << std::endl;
std::cout << " run [args...] Run the tool with arguments" << std::endl;
std::cout << " version Show version information" << std::endl;
std::cout << " help Show this help message" << std::endl;
std::cout << " run --argLine <value> Run the tool with a single string argument" << std::endl;
std::cout << " version Show version information" << std::endl;
std::cout << " help Show this help message" << std::endl;
}
void ShowRunHelp()
{
std::cout << "Usage: ohos-example run [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --argLine <value> Argument string to pass to the tool" << std::endl;
std::cout << " --help, -h Show this help message" << std::endl;
}
void ShowVersionHelp()
{
std::cout << "Usage: ohos-example version [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --help, -h Show this help message" << std::endl;
}
int RunCommand(int argc, char* argv[])
{
std::string argLine;
int i = 2;
while (i < argc) {
std::string arg = argv[i];
if (arg == "--help" || arg == "-h") {
ShowRunHelp();
return 0;
}
if (arg == "--argLine") {
if (i + 1 >= argc) {
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'argLine'.",
"Use: ohos-example run --argLine <value>");
return 1;
}
++i;
argLine = argv[i];
++i;
continue;
}
EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "' for subcommand 'run'.",
"Use: ohos-example run --argLine <value>");
return 1;
}
if (argLine.empty()) {
EmitError("ERR_MISSING_PARAM", "Missing required parameter 'argLine'.",
"Use: ohos-example run --argLine <value>");
return 1;
}
EmitProgress(0, "starting");
EmitProgress(50, "running");
EmitProgress(PROGRESS_MAX, "completed");
EmitSuccessResult("{\"result\":\"执行完成 " + EscapeJson(argLine) + "\"}");
return 0;
}
int VersionCommand(int argc, char* argv[])
{
if (argc == 3) {
std::string arg = argv[2];
if (arg == "--help" || arg == "-h") {
ShowVersionHelp();
return 0;
}
}
if (argc != 2) {
EmitError("ERR_UNKNOWN_PARAM", "Subcommand 'version' does not accept extra parameters.",
"Use: ohos-example version");
return 1;
}
EmitSuccessResult("{\"version\":\"" + std::string(VERSION) + "\",\"build_time\":\"" +
std::string(BUILD_TIME) + "\"}");
return 0;
}
int main(int argc, char* argv[])
{
if (argc < MIN_ARGC) {
ShowHelp();
EmitError("ERR_MISSING_PARAM", "Missing required subcommand.",
"Use one of: ohos-example run --argLine <value>, ohos-example version");
return 1;
}
std::string subcommand = argv[1];
if (subcommand == "run") {
std::vector<std::string> args;
for (int i = 2; i < argc; ++i) {
args.push_back(argv[i]);
}
return RunCommand(args);
} else if (subcommand == "version") {
return VersionCommand();
} else if (subcommand == "help" || subcommand == "--help" || subcommand == "-h") {
return RunCommand(argc, argv);
}
if (subcommand == "version") {
return VersionCommand(argc, argv);
}
if (subcommand == "help" || subcommand == "--help" || subcommand == "-h") {
ShowHelp();
return 0;
} else {
ShowHelp();
return 1;
}
return 0;
EmitError("ERR_INVALID_PARAM", "Unknown subcommand '" + subcommand + "'.",
"Use one of: run, version, help");
return 1;
}
+1 -5
View File
@@ -32,10 +32,6 @@
"type": "object",
"description": "Tool execution result",
"properties": {
"status": {
"type": "string",
"description": "Execution status, success when completed successfully"
},
"message": {
"type": "string",
"description": "Processed output message"
@@ -45,6 +41,6 @@
"description": "Actual repeat count"
}
},
"required": ["status", "message", "repeat_count"]
"required": ["message", "repeat_count"]
}
}
+122 -56
View File
@@ -11,76 +11,136 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cerrno>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <string>
#include <cstdlib>
// Constants for magic numbers
namespace {
constexpr int MESSAGE_PREFIX_LEN = 10;
constexpr int COUNT_PREFIX_LEN = 8;
constexpr int MAX_COUNT = 10;
constexpr int MIN_COUNT = 1;
constexpr int MAX_COUNT = 10;
constexpr int MIN_COUNT = 1;
}
void EmitResult(const std::string& status, const std::string& message, int repeatCount)
struct SimpleConfig {
std::string message = "Hello from ohos-simple";
int count = 1;
bool verbose = false;
};
std::string EscapeJson(const std::string& input)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"" << status << "\", "
<< "\"data\": {"
<< "\"status\": \"" << status << "\", "
<< "\"message\": \"" << message << "\", "
<< "\"repeat_count\": " << repeatCount
<< "}}"
<< "}" << std::endl;
std::string escaped;
escaped.reserve(input.size());
for (char ch : input) {
switch (ch) {
case '\\':
escaped += "\\\\";
break;
case '"':
escaped += "\\\"";
break;
case '\n':
escaped += "\\n";
break;
case '\r':
escaped += "\\r";
break;
case '\t':
escaped += "\\t";
break;
default:
escaped += ch;
break;
}
}
return escaped;
}
void EmitSuccessResult(const std::string& dataJson)
{
std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":"
<< dataJson << "}" << std::endl;
}
void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"failed\", "
<< "\"errCode\": \"" << errCode << "\", "
<< "\"errMsg\": \"" << errMsg << "\", "
<< "\"suggestion\": \"" << suggestion << "\""
<< "}" << std::endl;
std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\""
<< EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg)
<< "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl;
}
void ShowHelp()
{
std::cout << "Usage: ohos-simple [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --message=<msg> Set message to display (default: 'Hello from ohos-simple')" << std::endl;
std::cout << " --count=<num> Number of repetitions (1-10, default: 1)" << std::endl;
std::cout << " --message <msg> Set message to display (default: 'Hello from ohos-simple')" << std::endl;
std::cout << " --count <num> Number of repetitions (1-10, default: 1)" << std::endl;
std::cout << " --verbose Enable verbose output" << std::endl;
std::cout << " --help, -h Show this help message" << std::endl;
}
bool ParseArguments(int argc, char* argv[], std::string& message, int& count, bool& verbose)
bool ParseInteger(const std::string& value, int& result)
{
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg.find("--message=") == 0) {
message = arg.substr(MESSAGE_PREFIX_LEN);
} else if (arg.find("--count=") == 0) {
count = std::atoi(arg.substr(COUNT_PREFIX_LEN).c_str());
if (count < MIN_COUNT) {
count = MIN_COUNT;
}
if (count > MAX_COUNT) {
count = MAX_COUNT;
}
} else if (arg == "--verbose") {
verbose = true;
} else if (arg == "--help" || arg == "-h") {
ShowHelp();
return false;
}
char* end = nullptr;
errno = 0;
long parsed = std::strtol(value.c_str(), &end, 10);
if (errno != 0 || end == value.c_str() || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) {
return false;
}
result = static_cast<int>(parsed);
return true;
}
std::string ExecuteTask(const std::string& message, int count, bool verbose)
int ParseArguments(int argc, char* argv[], SimpleConfig& config)
{
int i = 1;
while (i < argc) {
std::string arg = argv[i];
if (arg == "--message") {
if (i + 1 >= argc) {
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'message'.",
"Use: ohos-simple --message <msg> [--count <num>] [--verbose]");
return 1;
}
++i;
config.message = argv[i];
++i;
continue;
}
if (arg == "--count") {
if (i + 1 >= argc) {
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'count'.",
"Use: ohos-simple --count <num> [--message <msg>] [--verbose]");
return 1;
}
++i;
if (!ParseInteger(argv[i], config.count)) {
EmitError("ERR_INVALID_PARAM", "Parameter 'count' must be an integer.",
"Use an integer between 1 and 10, for example: --count 2");
return 1;
}
++i;
continue;
}
if (arg == "--verbose") {
config.verbose = true;
++i;
continue;
}
if (arg == "--help" || arg == "-h") {
ShowHelp();
return 2;
}
EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "'.",
"Supported parameters are: --message, --count, --verbose");
return 1;
}
return 0;
}
std::string ExecuteTask(const std::string& message, int count)
{
std::string result;
for (int i = 0; i < count; ++i) {
@@ -89,27 +149,33 @@ std::string ExecuteTask(const std::string& message, int count, bool verbose)
}
result += message;
}
return result;
}
int main(int argc, char* argv[])
{
std::string message = "Hello from ohos-simple";
int count = 1;
bool verbose = false;
if (!ParseArguments(argc, argv, message, count, verbose)) {
return 0;
SimpleConfig config;
int parseResult = ParseArguments(argc, argv, config);
if (parseResult != 0) {
return parseResult == 2 ? 0 : 1;
}
if (message.empty()) {
if (config.message.empty()) {
EmitError("ERR_INVALID_PARAM", "Parameter 'message' must not be empty.",
"Provide a non-empty string, for example: --message hello");
return 1;
}
if (config.count < MIN_COUNT || config.count > MAX_COUNT) {
EmitError("ERR_INVALID_PARAM", "Parameter 'count' must be between 1 and 10.",
"Use an integer between 1 and 10, for example: --count 2");
return 1;
}
std::string result = ExecuteTask(message, count, verbose);
EmitResult("success", result, count);
std::string result = ExecuteTask(config.message, config.count);
if (config.verbose) {
result = "[verbose] " + result;
}
EmitSuccessResult("{\"message\":\"" + EscapeJson(result) + "\",\"repeat_count\":" +
std::to_string(config.count) + "}");
return 0;
}
+2 -6
View File
@@ -37,10 +37,6 @@
"type": "object",
"description": "Tool output result",
"properties": {
"status": {
"type": "string",
"description": "Execution status"
},
"duration": {
"type": "integer",
"description": "Planned duration (seconds)"
@@ -50,7 +46,7 @@
"description": "Actual duration (seconds)"
}
},
"required": ["status", "duration", "actual_duration"]
"required": ["duration", "actual_duration"]
},
"eventSchemas": {
"progress": {
@@ -71,7 +67,7 @@
"description": "Current status"
}
},
"required": ["percentage", "status"]
"required": ["type", "percentage", "status"]
}
}
}
+139 -124
View File
@@ -11,25 +11,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cerrno>
#include <chrono>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <thread>
#include <cstdlib>
#include <cerrno>
#include <climits>
// Constants for magic numbers
namespace {
constexpr int PROGRESS_PERCENTAGE_MAX = 100;
constexpr int DEFAULT_INTERVAL = 1;
constexpr int MIN_DURATION = 1;
constexpr int MIN_INTERVAL = 1;
constexpr int DURATION_PREFIX_LEN = 11;
constexpr int INTERVAL_PREFIX_LEN = 11;
constexpr int HELP_ARGC = 2;
constexpr int ARG_PARSE_START_INDEX = 1;
constexpr int PROGRESS_MAX = 100;
constexpr int DEFAULT_INTERVAL = 1;
constexpr int MIN_DURATION = 1;
constexpr int MIN_INTERVAL = 1;
}
struct TimerConfig {
@@ -39,119 +33,137 @@ struct TimerConfig {
bool verbose = false;
};
void EmitProgress(int percentage, const std::string& status)
std::string EscapeJson(const std::string& input)
{
std::cout << "{\"type\": \"progress\", "
<< "\"percentage\": " << percentage << ", "
<< "\"status\": \"" << status << "\""
<< "}" << std::endl;
std::string escaped;
escaped.reserve(input.size());
for (char ch : input) {
switch (ch) {
case '\\':
escaped += "\\\\";
break;
case '"':
escaped += "\\\"";
break;
case '\n':
escaped += "\\n";
break;
case '\r':
escaped += "\\r";
break;
case '\t':
escaped += "\\t";
break;
default:
escaped += ch;
break;
}
}
return escaped;
}
void EmitResult(const std::string& status, int duration, int actualDuration)
void EmitProgress(int percentage, const std::string& status)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"" << status << "\", "
<< "\"data\": {"
<< "\"status\": \"" << status << "\", "
<< "\"duration\": " << duration << ", "
<< "\"actual_duration\": " << actualDuration
<< "}}"
<< "}" << std::endl;
std::cout << "{\"type\":\"progress\",\"percentage\":" << percentage
<< ",\"status\":\"" << EscapeJson(status) << "\"}" << std::endl;
}
void EmitSuccessResult(const std::string& dataJson)
{
std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":"
<< dataJson << "}" << std::endl;
}
void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion)
{
std::cout << "{\"type\": \"result\", "
<< "\"status\": \"failed\", "
<< "\"errCode\": \"" << errCode << "\", "
<< "\"errMsg\": \"" << errMsg << "\", "
<< "\"suggestion\": \"" << suggestion << "\""
<< "}" << std::endl;
std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\""
<< EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg)
<< "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl;
}
void ShowHelp()
{
std::cout << "Usage: ohos-timer [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --duration=<sec> Duration in seconds (required, minimum 1)" << std::endl;
std::cout << " --interval=<sec> Progress update interval in seconds (optional, default 1)" << std::endl;
std::cout << " --progress Enable progress events" << std::endl;
std::cout << " --verbose Enable verbose output" << std::endl;
std::cout << " --help, -h Show this help message" << std::endl;
std::cout << " --duration <sec> Duration in seconds (required, minimum 1)" << std::endl;
std::cout << " --interval <sec> Progress update interval in seconds (default 1)" << std::endl;
std::cout << " --showProgress Enable progress events" << std::endl;
std::cout << " --verbose Enable verbose mode" << std::endl;
std::cout << " --help, -h Show this help message" << std::endl;
}
bool ParseArguments(int argc, char* argv[], TimerConfig& config)
bool ParseInteger(const std::string& value, int& result)
{
for (int i = ARG_PARSE_START_INDEX; i < argc; ++i) {
char* end = nullptr;
errno = 0;
long parsed = std::strtol(value.c_str(), &end, 10);
if (errno != 0 || end == value.c_str() || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) {
return false;
}
result = static_cast<int>(parsed);
return true;
}
int ParseArguments(int argc, char* argv[], TimerConfig& config)
{
int i = 1;
while (i < argc) {
std::string arg = argv[i];
if (arg.find("--duration=") == 0) {
std::string value = arg.substr(DURATION_PREFIX_LEN);
char* end = nullptr;
errno = 0;
long val = std::strtol(value.c_str(), &end, 10);
if (errno != 0 || end == value.c_str() || *end != '\0' || val < 0 || val > INT_MAX) {
return false;
if (arg == "--duration") {
if (i + 1 >= argc) {
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'duration'.",
"Use: ohos-timer --duration <sec> [--interval <sec>] [--showProgress] [--verbose]");
return 1;
}
config.duration = static_cast<int>(val);
} else if (arg.find("--interval=") == 0) {
std::string value = arg.substr(INTERVAL_PREFIX_LEN);
char* end = nullptr;
errno = 0;
long val = std::strtol(value.c_str(), &end, 10);
if (errno != 0 || end == value.c_str() || *end != '\0' || val < 0 || val > INT_MAX) {
return false;
++i;
if (!ParseInteger(argv[i], config.duration)) {
EmitError("ERR_INVALID_PARAM", "Parameter 'duration' must be an integer.",
"Use a positive integer, for example: --duration 5");
return 1;
}
config.interval = static_cast<int>(val);
} else if (arg == "--progress") {
config.showProgress = true;
} else if (arg == "--verbose") {
config.verbose = true;
} else if (arg == "--help" || arg == "-h") {
ShowHelp();
return false;
++i;
continue;
}
if (arg == "--interval") {
if (i + 1 >= argc) {
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'interval'.",
"Use: ohos-timer --interval <sec> [--duration <sec>] [--showProgress] [--verbose]");
return 1;
}
++i;
if (!ParseInteger(argv[i], config.interval)) {
EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must be an integer.",
"Use a positive integer, for example: --interval 1");
return 1;
}
++i;
continue;
}
if (arg == "--showProgress") {
config.showProgress = true;
++i;
continue;
}
if (arg == "--verbose") {
config.verbose = true;
++i;
continue;
}
if (arg == "--help" || arg == "-h") {
ShowHelp();
return 2;
}
EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "'.",
"Supported parameters are: --duration, --interval, --showProgress, --verbose");
return 1;
}
return true;
}
bool ValidateArguments(const TimerConfig& config)
{
if (config.duration < MIN_DURATION) {
return false;
}
if (config.interval < MIN_INTERVAL) {
return false;
}
if (config.interval > config.duration) {
return false;
}
return true;
}
void UpdateProgress(int elapsed, int duration, int& lastPercentage)
{
if (duration == 0) {
return;
}
int percentage = static_cast<int>(
(elapsed * PROGRESS_PERCENTAGE_MAX) / duration
);
if (percentage > lastPercentage && percentage > 0) {
EmitProgress(percentage, "running");
lastPercentage = percentage;
}
return 0;
}
int ExecuteTimer(const TimerConfig& config)
{
auto startTime = std::chrono::steady_clock::now();
int lastPercentage = -1;
int elapsed = 0;
if (config.showProgress) {
@@ -159,46 +171,49 @@ int ExecuteTimer(const TimerConfig& config)
}
while (elapsed < config.duration) {
if (config.showProgress) {
UpdateProgress(elapsed, config.duration, lastPercentage);
}
std::this_thread::sleep_for(std::chrono::seconds(config.interval));
elapsed = static_cast<int>(
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - startTime
).count()
);
elapsed = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - startTime).count());
if (config.showProgress && elapsed < config.duration) {
int percentage = elapsed * PROGRESS_MAX / config.duration;
EmitProgress(percentage, config.verbose ? "running (verbose)" : "running");
}
}
if (config.showProgress) {
EmitProgress(PROGRESS_PERCENTAGE_MAX, "completed");
EmitProgress(PROGRESS_MAX, "completed");
}
EmitResult("success", config.duration, elapsed);
EmitSuccessResult("{\"duration\":" + std::to_string(config.duration) +
",\"actual_duration\":" + std::to_string(elapsed) + "}");
return 0;
}
int main(int argc, char* argv[])
{
TimerConfig config;
if (argc < HELP_ARGC) {
ShowHelp();
return 1;
}
if (!ParseArguments(argc, argv, config)) {
return 1;
int parseResult = ParseArguments(argc, argv, config);
if (parseResult != 0) {
return parseResult == 2 ? 0 : 1;
}
if (config.duration == 0) {
ShowHelp();
EmitError("ERR_MISSING_PARAM", "Missing required parameter 'duration'.",
"Use: ohos-timer --duration <sec> [--interval <sec>] [--showProgress] [--verbose]");
return 1;
}
if (!ValidateArguments(config)) {
if (config.duration < MIN_DURATION) {
EmitError("ERR_INVALID_PARAM", "Parameter 'duration' must be greater than or equal to 1.",
"Use a positive integer, for example: --duration 5");
return 1;
}
if (config.interval < MIN_INTERVAL) {
EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must be greater than or equal to 1.",
"Use a positive integer, for example: --interval 1");
return 1;
}
if (config.interval > config.duration) {
EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must not be greater than 'duration'.",
"Use values such as: --duration 5 --interval 1");
return 1;
}