1/*
2 * Copyright (c) 2022 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#include "shell_bas.h"
16#include <fcntl.h>
17#include <signal.h>
18
19#include "init_utils.h"
20#include "shell_utils.h"
21
22char *BShellEnvErrString(BShellHandle handle, int32_t err)
23{
24    static BShellErrInfo shellErrString[] = {
25        {BSH_SHELL_INFO, "\r\n\r\n"
26                        "+=========================================================+\r\n"
27                        "|               Parameter shell v"BSH_VERSION"                    |\r\n"
28                        "+=========================================================+\r\n"
29        },
30        {BSH_CMD_TOO_LONG, "\r\nWarnig: Command is too long\r\n"},
31        {BSH_SHOW_CMD_LIST, "Command list:\r\n"},
32        {BSH_CMD_NOT_EXIST, "Command not found\r\n"}
33    };
34    for (size_t i = 0; i < sizeof(shellErrString) / sizeof(shellErrString[0]); i++) {
35        if ((int32_t)shellErrString[i].err == err) {
36            return shellErrString[i].desc;
37        }
38    }
39    BSH_CHECK(handle != NULL, return "System unknown err", "Invalid shell env");
40    BShellEnv *shell = (BShellEnv *)handle;
41    int len = sprintf_s(shell->data, sizeof(shell->data) - 1, "System unknown err 0x%08x", err);
42    if (len <= 0) {
43        BSH_LOGE("Write shell data size failed.");
44    }
45    return shell->data;
46}
47
48static void BShellCmdOutputCmdHelp(BShellHandle handle, BShellCommand *cmd)
49{
50    BShellEnvOutputString(handle, "    ");
51    int32_t spaceLength = BShellEnvOutputString(handle, cmd->help);
52    spaceLength = BSH_CMD_NAME_END - spaceLength;
53    spaceLength = (spaceLength > 0) ? spaceLength : 4; // 4 min
54    do {
55        BShellEnvOutputString(handle, " ");
56    } while (--spaceLength);
57    BShellEnvOutputString(handle, "--");
58    BShellEnvOutputString(handle, cmd->desc);
59    BShellEnvOutputString(handle, "\r\n");
60}
61
62int32_t BShellCmdHelp(BShellHandle handle, int32_t argc, char *argv[])
63{
64    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
65    BShellEnv *shell = (BShellEnv *)handle;
66    BShellEnvOutputString(handle, BShellEnvErrString(handle, BSH_SHOW_CMD_LIST));
67
68    int show = 0;
69    BShellCommand *cmd = shell->command;
70    while (cmd != NULL) {
71        if ((argc >= 1) &&
72            (strncmp(argv[0], cmd->name, strlen(argv[0])) == 0) &&
73            (strncmp(argv[0], "help", strlen(argv[0])) != 0)) {
74            BShellCmdOutputCmdHelp(handle, cmd);
75            show = 1;
76        }
77        cmd = cmd->next;
78    }
79    if (show) {
80        return 0;
81    }
82    cmd = shell->command;
83    while (cmd != NULL) {
84        BShellCmdOutputCmdHelp(handle, cmd);
85        cmd = cmd->next;
86    }
87    return 0;
88}
89
90static int32_t BShellCmdExit(BShellHandle handle, int32_t argc, char *argv[])
91{
92#ifndef STARTUP_INIT_TEST
93    kill(getpid(), SIGINT);
94#endif
95    return 0;
96}
97
98int32_t BShellEnvOutput(BShellHandle handle, char *fmt, ...)
99{
100    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
101    va_list list;
102    va_start(list, fmt);
103    int len = vfprintf(stdout, fmt, list);
104    va_end(list);
105    (void)fflush(stdout);
106    return len;
107}
108
109int32_t BShellEnvOutputString(BShellHandle handle, const char *string)
110{
111    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
112    printf("%s", string);
113    (void)fflush(stdout);
114    return strlen(string);
115}
116
117int32_t BShellEnvOutputPrompt(BShellHandle handle, const char *prompt)
118{
119    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
120    BSH_CHECK(prompt != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
121    BShellEnv *shell = (BShellEnv *)handle;
122    if (shell->prompt != NULL) {
123        free(shell->prompt);
124    }
125    size_t promptLen = strlen(prompt);
126    if (promptLen > BSH_CMD_NAME_END) {
127        shell->prompt = strdup(prompt + promptLen - BSH_CMD_NAME_END);
128        if (shell->prompt != NULL) {
129            shell->prompt[0] = '.';
130            shell->prompt[1] = '.';
131            shell->prompt[2] = '.'; // 2 index
132        }
133    } else {
134        shell->prompt = strdup(prompt);
135        BSH_CHECK(shell->prompt != NULL, return BSH_INVALID_PARAM, "strdup prompt failed.");
136    }
137    return 0;
138}
139
140void BShellEnvOutputByte(BShellHandle handle, char data)
141{
142    BSH_CHECK(handle != NULL, return, "Invalid shell env");
143    printf("%c", data);
144    (void)fflush(stdout);
145}
146
147void BShellEnvOutputResult(BShellHandle handle, int32_t result)
148{
149    if (result == 0) {
150        return;
151    }
152    printf("result: 0x%08x\r\n", result);
153    (void)fflush(stdout);
154}
155
156static void BShellEnvOutputParam(BShellHandle handle, char *var)
157{
158    BShellEnvOutput(handle, (var[0] == '$') ? var + 1 : var);
159    BShellEnvOutputString(handle, " = ");
160    BShellEnvOutputString(handle, BShellEnvGetStringParam(handle, var));
161}
162
163void BShellEnvBackspace(BShellHandle handle, uint32_t length)
164{
165    for (uint32_t i = 0; i < length; i++) {
166        BShellEnvOutputString(handle, "\b \b");
167    }
168}
169
170static void BShellEnvParseParam(BShellEnv *shell)
171{
172    uint8_t quotes = 0;
173    uint8_t record = 1;
174    shell->argc = 0;
175    for (uint16_t i = 0; i < shell->length; i++) {
176        char data = *(shell->buffer + i);
177        if ((quotes != 0 || (data != ' ' && data != '\t' && data != ',')) && data != 0) {
178            if (data == '\"') {
179                quotes = quotes ? 0 : 1;
180            }
181            if (record == 1) {
182                BSH_CHECK(shell->argc < BSH_PARAMETER_MAX_NUMBER, return, "argc out of range");
183                shell->args[shell->argc++] = shell->buffer + i;
184                record = 0;
185            }
186            if (*(shell->buffer + i) == '\\' && *(shell->buffer + i + 1) != 0) {
187                i++;
188            }
189        } else {
190            *(shell->buffer + i) = 0;
191            record = 1;
192        }
193    }
194}
195
196static int32_t BShellEnvExcuteCmd(BShellEnv *shell, BShellCommand *cmd)
197{
198    return cmd->executer((BShellHandle)shell, shell->argc - cmd->argStart, &shell->args[cmd->argStart]);
199}
200
201static int32_t BShellEnvHandleEnter(BShellHandle handle, uint8_t data)
202{
203    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
204    BShellEnv *shell = (BShellEnv *)handle;
205    if (shell->length == 0) {
206        BShellEnvOutputString(shell, "\n");
207        BShellEnvOutputString(handle, shell->prompt);
208        return 0;
209    }
210
211    *(shell->buffer + shell->length++) = 0;
212    BShellEnvParseParam(shell);
213    shell->length = 0;
214    shell->cursor = 0;
215    if (shell->argc == 0) {
216        BShellEnvOutputString(shell, shell->prompt);
217        return 0;
218    }
219
220    BShellEnvOutputString(shell, "\n");
221    if (strcmp((const char *)shell->args[0], "help") == 0) {
222        BShellCmdHelp(handle, shell->argc, shell->args);
223        BShellEnvOutputString(shell, shell->prompt);
224        return 0;
225    }
226    if (shell->args[0][0] == '$') {
227        BShellEnvOutputParam(shell, shell->args[0]);
228        BShellEnvOutputString(shell, shell->prompt);
229        return 0;
230    }
231
232    BShellCommand *cmd = BShellEnvGetCmd(handle, (uint32_t)shell->argc, shell->args);
233    if (cmd != NULL) {
234        int32_t ret = BShellEnvExcuteCmd(shell, cmd);
235        BShellEnvOutputResult(shell, ret);
236    } else {
237        BShellEnvOutputString(shell, BShellEnvErrString(handle, BSH_CMD_NOT_EXIST));
238    }
239    BShellEnvOutputString(shell, shell->prompt);
240    return 0;
241}
242
243static int32_t BShellEnvHandleBackspace(BShellHandle handle, uint8_t code)
244{
245    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
246    BShellEnv *shell = (BShellEnv *)handle;
247    if (shell->length == 0) {
248        return 0;
249    }
250    if (shell->cursor == shell->length) {
251        shell->length--;
252        shell->cursor--;
253        shell->buffer[shell->length] = 0;
254        BShellEnvBackspace(handle, 1);
255    } else if (shell->cursor > 0) {
256        for (short i = 0; i < shell->length - shell->cursor; i++) {
257            shell->buffer[shell->cursor + i - 1] = shell->buffer[shell->cursor + i];
258        }
259        shell->length--;
260        shell->cursor--;
261        shell->buffer[shell->length] = 0;
262        BShellEnvOutputByte(shell, '\b');
263        for (short i = shell->cursor; i < shell->length; i++) {
264            BShellEnvOutputByte(shell, shell->buffer[i]);
265        }
266        BShellEnvOutputByte(shell, ' ');
267        for (short i = shell->length - shell->cursor + 1; i > 0; i--) {
268            BShellEnvOutputByte(shell, '\b');
269        }
270    }
271    return 0;
272}
273
274static int32_t BShellEnvHandleTab(BShellHandle handle, uint8_t code)
275{
276    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
277    return 0;
278}
279
280static void BShellEnvHandleNormal(BShellHandle handle, uint8_t data)
281{
282    BSH_CHECK(handle != NULL, return, "Invalid shell env");
283    BShellEnv *shell = (BShellEnv *)handle;
284    if (data == 0) {
285        return;
286    }
287    if (shell->length < BSH_COMMAND_MAX_LENGTH - 1) {
288        if (shell->length == shell->cursor) {
289            shell->buffer[shell->length++] = data;
290            shell->cursor++;
291            BShellEnvOutputByte(shell, data);
292        } else {
293            for (uint16_t i = shell->length - shell->cursor; i > 0; i--) {
294                shell->buffer[shell->cursor + i] = shell->buffer[shell->cursor + i - 1];
295            }
296            shell->buffer[shell->cursor++] = data;
297            shell->buffer[++shell->length] = 0;
298            for (uint16_t i = shell->cursor - 1; i < shell->length; i++) {
299                BShellEnvOutputByte(shell, shell->buffer[i]);
300            }
301            for (uint16_t i = shell->length - shell->cursor; i > 0; i--) {
302                BShellEnvOutputByte(shell, '\b');
303            }
304        }
305    } else {
306        BShellEnvOutputString(shell, BShellEnvErrString(handle, BSH_CMD_TOO_LONG));
307        BShellEnvOutputString(shell, shell->prompt);
308
309        shell->cursor = shell->length;
310    }
311}
312
313static int32_t BShellEnvHandleCtrC(BShellHandle handle, uint8_t code)
314{
315    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
316    BSH_LOGV("BShellEnvHandleCtrC %d", getpid());
317#ifndef STARTUP_INIT_TEST
318    kill(getpid(), SIGKILL);
319#endif
320    return 0;
321}
322
323static int32_t BShellEnvHandleEsc(BShellHandle handle, uint8_t code)
324{
325    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
326    BShellEnv *shell = (BShellEnv *)handle;
327    shell->shellState = BSH_ANSI_ESC;
328    return 0;
329}
330
331BShellKey *BShellEnvGetDefaultKey(uint8_t code)
332{
333    static BShellKey defaultKeys[] = {
334        {BSH_KEY_LF, BShellEnvHandleEnter, NULL},
335        {BSH_KEY_CR, BShellEnvHandleEnter, NULL},
336        {BSH_KEY_TAB, BShellEnvHandleTab, NULL},
337        {BSH_KEY_BACKSPACE, BShellEnvHandleBackspace, NULL},
338        {BSH_KEY_DELETE, BShellEnvHandleBackspace, NULL},
339        {BSH_KEY_CTRLC, BShellEnvHandleCtrC, NULL},
340        {BSH_KEY_ESC, BShellEnvHandleEsc, NULL},
341    };
342    for (size_t i = 0; i < sizeof(defaultKeys) / sizeof(defaultKeys[0]); i++) {
343        if (defaultKeys[i].keyCode == code) {
344            return &defaultKeys[i];
345        }
346    }
347    return NULL;
348}
349
350SHELLSTATIC void BShellEnvProcessInput(BShellHandle handle, char data)
351{
352    BSH_CHECK(handle != NULL, return, "Invalid shell env");
353    BShellEnv *shell = (BShellEnv *)handle;
354    if (shell->shellState == BSH_IN_NORMAL) {
355        BShellKey *key = BShellEnvGetKey(handle, data);
356        if (key != NULL) {
357            key->keyHandle(shell, (uint8_t)data);
358            return;
359        }
360        key = BShellEnvGetDefaultKey(data);
361        if (key != NULL) {
362            key->keyHandle(shell, (uint8_t)data);
363            return;
364        }
365        BShellEnvHandleNormal(shell, data);
366    } else if (shell->shellState == BSH_ANSI_CSI) {
367        switch (data) {
368            case 0x41:  // up
369                break;
370            case 0x42:  // down
371                break;
372            case 0x43:  // ->
373                if (shell->cursor < shell->length) {
374                    BShellEnvOutputByte(handle, shell->buffer[shell->cursor]);
375                    shell->cursor++;
376                }
377                break;
378            case 0x44:  // <-
379                if (shell->cursor > 0) {
380                    BShellEnvOutputByte(shell, '\b');
381                    shell->cursor--;
382                }
383                break;
384            default:
385                break;
386        }
387        shell->shellState = BSH_IN_NORMAL;
388    } else if (shell->shellState == BSH_ANSI_ESC) {
389        if (data == 0x5B) { // input up/down
390            shell->shellState = BSH_ANSI_CSI;
391        } else {
392            shell->shellState = BSH_IN_NORMAL;
393        }
394    }
395}
396
397void BShellEnvLoop(BShellHandle handle)
398{
399    BSH_CHECK(handle != NULL, return, "Invalid shell env");
400    BShellEnv *shell = (BShellEnv *)handle;
401    BSH_CHECK(shell->input != NULL, return, "Invalid shell input");
402    while (1) {
403        char data = 0;
404        if (shell->input(&data, 1) == 1) {
405            BShellEnvProcessInput(shell, data);
406        }
407    }
408}
409
410int32_t BShellEnvInit(BShellHandle *handle, const BShellInfo *info)
411{
412    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
413    BSH_CHECK(info != NULL && info->prompt != NULL, return BSH_INVALID_PARAM, "Invalid cmd name");
414
415    BShellEnv *shell = (BShellEnv *)calloc(1, sizeof(BShellEnv));
416    BSH_CHECK(shell != NULL, return BSH_INVALID_PARAM, "Failed to create shell env");
417    shell->length = 0;
418    shell->cursor = 0;
419    shell->shellState = BSH_IN_NORMAL;
420    shell->input = info->input;
421    shell->prompt = strdup(info->prompt);
422    BSH_CHECK(shell->prompt != NULL, free(shell); return BSH_INVALID_PARAM, "Failed to strdup prompt");
423    shell->command = NULL;
424    shell->param = NULL;
425    shell->keyHandle = NULL;
426    shell->execMode = BSH_EXEC_INDEPENDENT;
427    *handle = (BShellHandle)shell;
428    return 0;
429}
430
431int BShellEnvStart(BShellHandle handle)
432{
433    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
434    BShellEnv *shell = (BShellEnv *)handle;
435    shell->execMode = BSH_EXEC_TASK;
436    BShellEnvOutputString(handle, BShellEnvErrString(handle, BSH_SHELL_INFO));
437    BShellEnvOutputString(handle, shell->prompt);
438
439    const CmdInfo infos[] = {
440        {"exit", BShellCmdExit, "exit parameter shell", "exit"},
441        {"help", BShellCmdHelp, "help command", "help"}
442    };
443    for (size_t i = 0; i < sizeof(infos) / sizeof(infos[0]); i++) {
444        BShellEnvRegisterCmd(handle, &infos[i]);
445    }
446    return 0;
447}
448
449static void BShellParamFree(BShellParam *param)
450{
451    if (param->desc != NULL) {
452        free(param->desc);
453    }
454    if (param->type == PARAM_STRING && param->value.string != NULL) {
455        free(param->value.string);
456    }
457    free(param);
458}
459
460static void BShellCmdFree(BShellCommand *cmd)
461{
462    if (cmd->desc != NULL) {
463        free(cmd->desc);
464        cmd->desc = NULL;
465    }
466    if (cmd->help != NULL) {
467        free(cmd->help);
468        cmd->help = NULL;
469    }
470    if (cmd->multikey != NULL) {
471        free(cmd->multikey);
472        cmd->multikey = NULL;
473    }
474    free(cmd);
475}
476
477void BShellEnvDestory(BShellHandle handle)
478{
479    BSH_CHECK(handle != NULL, return, "Invalid shell env");
480    BShellEnv *shell = (BShellEnv *)handle;
481
482    BShellCommand *cmd = shell->command;
483    while (cmd != NULL) {
484        shell->command = cmd->next;
485        BShellCmdFree(cmd);
486        cmd = shell->command;
487    }
488
489    BShellParam *param = shell->param;
490    while (param != NULL) {
491        shell->param = param->next;
492        BShellParamFree(param);
493        param = shell->param;
494    }
495
496    BShellKey *key = shell->keyHandle;
497    while (key != NULL) {
498        shell->keyHandle = key->next;
499        free(key);
500        key = shell->keyHandle;
501    }
502    if (shell->prompt != NULL) {
503        free(shell->prompt);
504    }
505    free(shell);
506}
507
508int32_t BShellEnvRegisterCmd(BShellHandle handle, const CmdInfo *cmdInfo)
509{
510    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
511    BSH_CHECK(cmdInfo != NULL && cmdInfo->name != NULL, return BSH_INVALID_PARAM, "Invalid cmd name");
512    BSH_CHECK(cmdInfo->executer != NULL, return BSH_INVALID_PARAM, "Invalid cmd executer");
513    BShellEnv *shell = (BShellEnv *)handle;
514    size_t nameLen = strlen(cmdInfo->name) + 1;
515    BShellCommand *cmd = (BShellCommand *)calloc(1, sizeof(BShellCommand) + nameLen);
516    BSH_CHECK(cmd != NULL, return BSH_INVALID_PARAM, "Failed to alloc cmd name %s", cmdInfo->name);
517    cmd->executer = cmdInfo->executer;
518    cmd->argStart = 0;
519    int32_t ret = 0;
520    do {
521        ret = strcpy_s(cmd->name, nameLen, cmdInfo->name);
522        BSH_CHECK(ret == 0, break, "Failed to copy name %s", cmdInfo->name);
523
524        ret = BSH_SYSTEM_ERR;
525        if (cmdInfo->desc != NULL) {
526            cmd->desc = strdup(cmdInfo->desc);
527            BSH_CHECK(cmd->desc != NULL, break, "Failed to copy desc %s", cmdInfo->name);
528        }
529        if (cmdInfo->help != NULL) {
530            cmd->help = strdup(cmdInfo->help);
531            BSH_CHECK(cmd->help != NULL, break, "Failed to copy help %s", cmdInfo->name);
532        }
533        cmd->multikey = NULL;
534        if (cmdInfo->multikey != NULL && strlen(cmdInfo->multikey) > nameLen) {
535            cmd->multikey = strdup(cmdInfo->multikey);
536            BSH_CHECK(cmd->multikey != NULL, break, "Failed to copy multikey %s", cmdInfo->name);
537            int argc = SplitString(cmd->multikey, " ", cmd->multikeys, (int)ARRAY_LENGTH(cmd->multikeys));
538            BSH_CHECK(argc <= (int)ARRAY_LENGTH(cmd->multikeys) && argc > 0, break, "Invalid multikey");
539            cmd->argStart = argc - 1;
540            if (argc == 1) {
541                free(cmd->multikey);
542                cmd->multikey = NULL;
543            }
544        }
545        ret = 0;
546    } while (0);
547    if (ret != 0) {
548        BShellCmdFree(cmd);
549        return ret;
550    }
551    cmd->next = shell->command;
552    shell->command = cmd;
553    return 0;
554}
555
556static const char *GetRealCmdName(const char *name)
557{
558    int i = 0;
559    int last = 0;
560    while (*(name + i) != '\0') {
561        if (*(name + i) == '/') {
562            last = i;
563        }
564        i++;
565    }
566    if (last != 0) {
567        return name + last + 1;
568    } else {
569        return name;
570    }
571}
572
573BShellCommand *BShellEnvGetCmd(BShellHandle handle, int32_t argc, char *argv[])
574{
575    BSH_CHECK(handle != NULL, return NULL, "Invalid shell env");
576    BSH_CHECK(argc >= 1, return NULL, "Invalid argc");
577    const char *cmdName = GetRealCmdName(argv[0]);
578    BSH_LOGV("BShellEnvGetCmd %s cmd %s", argv[0], cmdName);
579    BShellEnv *shell = (BShellEnv *)handle;
580    BShellCommand *cmd = shell->command;
581    while (cmd != NULL) {
582        if (strcmp(cmd->name, cmdName) != 0) {
583            cmd = cmd->next;
584            continue;
585        }
586        if (cmd->multikey == NULL) {
587            return cmd;
588        }
589        int32_t i = 0;
590        for (i = 0; i < (int32_t)ARRAY_LENGTH(cmd->multikeys) && i < argc; i++) {
591            if (cmd->multikeys[i] == NULL) {
592                return cmd;
593            }
594            char *tmp = argv[i];
595            if (i == 0) {
596                tmp = (char *)cmdName;
597            }
598            if (strcmp(cmd->multikeys[i], tmp) != 0) {
599                break;
600            }
601        }
602        if (i >= (int32_t)ARRAY_LENGTH(cmd->multikeys)) {
603            return cmd;
604        }
605        if (i >= argc) {
606            if (cmd->multikeys[i] == NULL) {
607                return cmd;
608            }
609        }
610        cmd = cmd->next;
611    }
612    return NULL;
613}
614
615int32_t BShellEnvRegisterKeyHandle(BShellHandle handle, uint8_t code, BShellkeyHandle keyHandle)
616{
617    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
618    BSH_CHECK(keyHandle != NULL, return BSH_INVALID_PARAM, "Invalid cmd name");
619    BShellEnv *shell = (BShellEnv *)handle;
620
621    BShellKey *key = (BShellKey *)calloc(1, sizeof(BShellKey));
622    BSH_CHECK(key != NULL, return BSH_INVALID_PARAM, "Failed to alloc key code %d", code);
623    key->keyCode = code;
624    key->keyHandle = keyHandle;
625    key->next = shell->keyHandle;
626    shell->keyHandle = key;
627    return 0;
628}
629
630BShellKey *BShellEnvGetKey(BShellHandle handle, uint8_t code)
631{
632    BSH_CHECK(handle != NULL, return NULL, "Invalid shell env");
633    BShellEnv *shell = (BShellEnv *)handle;
634    BShellKey *key = shell->keyHandle;
635    while (key != NULL) {
636        if (key->keyCode == code) {
637            return key;
638        }
639        key = key->next;
640    }
641    return NULL;
642}
643
644static int32_t BShellParamSetValue(BShellParam *param, void *value)
645{
646    static uint32_t paramValueLens[] = {
647        sizeof(uint8_t), sizeof(uint16_t), sizeof(uint32_t), sizeof(char *)
648    };
649    if (param->type == PARAM_STRING) {
650        if (param->value.string != NULL) {
651            free(param->value.string);
652        }
653        param->value.string = strdup((char *)value);
654        BSH_CHECK(param->value.string != NULL, return BSH_SYSTEM_ERR, "Failed to copy value for %s", param->name);
655    } else if (param->type < PARAM_STRING) {
656        int ret = memcpy_s(&param->value, sizeof(param->value), value, paramValueLens[param->type]);
657        BSH_CHECK(ret == 0, return BSH_SYSTEM_ERR, "Failed to copy value for %s", param->name);
658    }
659    return 0;
660}
661
662int32_t BShellEnvSetParam(BShellHandle handle, const char *name, const char *desc, BShellParamType type, void *value)
663{
664    BSH_CHECK(handle != NULL, return BSH_INVALID_PARAM, "Invalid shell env");
665    BSH_CHECK(name != NULL, return BSH_INVALID_PARAM, "Invalid param name");
666    BSH_CHECK(value != NULL, return BSH_INVALID_PARAM, "Invalid cmd value");
667    BSH_CHECK(type <= PARAM_STRING, return BSH_INVALID_PARAM, "Invalid param type");
668    BShellEnv *shell = (BShellEnv *)handle;
669    const BShellParam *tmp = BShellEnvGetParam(handle, name);
670    if (tmp != NULL) {
671        BSH_CHECK(tmp->type <= type, return BSH_INVALID_PARAM, "Invalid param type %d", tmp->type);
672        return BShellParamSetValue((BShellParam *)tmp, value);
673    }
674    int32_t ret = 0;
675    BShellParam *param = NULL;
676    do {
677        size_t nameLen = strlen(name) + 1;
678        param = (BShellParam *)calloc(1, sizeof(BShellParam) + nameLen);
679        BSH_CHECK(param != NULL, return BSH_SYSTEM_ERR, "Failed to alloc cmd name %s", name);
680        param->type = type;
681        ret = strcpy_s(param->name, nameLen, name);
682        BSH_CHECK(ret == 0, break, "Failed to copy name %s", name);
683        if (desc != NULL) {
684            param->desc = strdup(desc);
685            BSH_CHECK(param->desc != NULL, free(param); return BSH_SYSTEM_ERR, "Failed to set desc");
686        }
687        ret = BShellParamSetValue(param, value);
688        BSH_CHECK(ret == 0, break, "Failed set value for %s", name);
689
690        param->next = shell->param;
691        shell->param = param;
692    } while (0);
693    if (ret != 0) {
694        BShellParamFree(param);
695    }
696    return ret;
697}
698
699const BShellParam *BShellEnvGetParam(BShellHandle handle, const char *name)
700{
701    BSH_CHECK(handle != NULL, return NULL, "Invalid shell env");
702    BShellEnv *shell = (BShellEnv *)handle;
703    BShellParam *param = shell->param;
704    while (param != NULL) {
705        if (strcmp(name, param->name) == 0) {
706            return param;
707        }
708        param = param->next;
709    }
710    return NULL;
711}
712
713const char *BShellEnvGetStringParam(BShellHandle handle, const char *name)
714{
715    BSH_CHECK(handle != NULL, return "", "Invalid shell env");
716    const BShellParam *param = BShellEnvGetParam(handle, name);
717    if (param == NULL) {
718        return "";
719    }
720    switch (param->type) {
721        case PARAM_STRING:
722            return param->value.string;
723        default:
724            break;
725    }
726    return "";
727}
728
729const ParamInfo *BShellEnvGetReservedParam(BShellHandle handle, const char *name)
730{
731    BSH_CHECK(handle != NULL, return NULL, "Invalid shell env");
732    static ParamInfo reservedParams[] = {
733        {PARAM_REVERESD_NAME_CURR_PARAMETER, "current parameter", PARAM_STRING}
734    };
735    for (size_t i = 0; i < sizeof(reservedParams) / sizeof(reservedParams[0]); i++) {
736        if (strcmp(name, reservedParams[i].name) == 0) {
737            return &reservedParams[i];
738        }
739    }
740    return NULL;
741}
742
743int32_t BShellEnvDirectExecute(BShellHandle handle, int argc, char *args[])
744{
745    BSH_CHECK(handle != NULL, return -1, "Invalid shell env");
746    BShellCommand *cmd = BShellEnvGetCmd(handle, argc, args);
747    if (cmd != NULL) {
748        int32_t ret = cmd->executer(handle, argc - cmd->argStart, &args[cmd->argStart]);
749        BShellEnvOutputResult(handle, ret);
750    } else {
751        if (argc > 1) {
752            BShellEnvOutputString(handle, BShellEnvErrString(handle, BSH_CMD_NOT_EXIST));
753        }
754        BShellCmdHelp(handle, argc, args);
755    }
756    return 0;
757}
758