#include "wsh_shell_cmd.h"
WSH_SHELL_RET_STATE_t WshShellCmd_Attach(WshShellCmd_Table_t* pShellCommands,
const WshShellCmd_t* pcCmdTable[],
WshShell_Size_t cmdNum) {
WSH_SHELL_ASSERT(pShellCommands && pcCmdTable && cmdNum > 0);
if (!pShellCommands || !pcCmdTable || cmdNum == 0)
return WSH_SHELL_RET_STATE_ERR_PARAM;
if (pShellCommands->List != NULL)
return WSH_SHELL_RET_STATE_ERR_BUSY; // Already inited
WSH_SHELL_RET_STATE_t retState = WSH_SHELL_RET_STATE_SUCCESS;
for (WshShell_Size_t cmd = 0; cmd < cmdNum; cmd++) {
const WshShellCmd_t* pcCmd = pcCmdTable[cmd];
WSH_SHELL_ASSERT(pcCmd != NULL);
const WshShellOption_t* pcOptOuter = pcCmd->Options;
for (; pcOptOuter->Type != WSH_SHELL_OPTION_END; pcOptOuter++) {
const WshShellOption_t* pcOptInner = pcOptOuter + 1;
for (; pcOptInner->Type != WSH_SHELL_OPTION_END; pcOptInner++) {
if (pcOptOuter->ShortName[0] && pcOptInner->ShortName[0] &&
strcmp(pcOptOuter->ShortName, pcOptInner->ShortName) == 0) {
WSH_SHELL_PRINT_ERR("Duplicate short option name detected: %s %s\r\n",
pcCmd->Name, pcOptInner->ShortName);
WSH_SHELL_ASSERT(0);
retState = WSH_SHELL_RET_STATE_ERROR;
}
if (pcOptOuter->LongName[0] && pcOptInner->LongName[0] &&
strcmp(pcOptOuter->LongName, pcOptInner->LongName) == 0) {
WSH_SHELL_PRINT_ERR("Duplicate long option name detected: %s %s\r\n",
pcCmd->Name, pcOptInner->LongName);
WSH_SHELL_ASSERT(0);
retState = WSH_SHELL_RET_STATE_ERROR;
}
}
}
}
pShellCommands->List = pcCmdTable;
pShellCommands->Num = cmdNum;
return retState;
}
void WshShellCmd_DeAttach(WshShellCmd_Table_t* pShellCommands) {
WSH_SHELL_ASSERT(pShellCommands);
if (pShellCommands)
*pShellCommands = (WshShellCmd_Table_t){0};
}
WshShell_Size_t WshShellCmd_GetCmdNum(WshShellCmd_Table_t* pShellCommands) {
WSH_SHELL_ASSERT(pShellCommands);
if (!pShellCommands || !pShellCommands->List)
return 0;
return pShellCommands->Num;
}
const WshShellCmd_t* WshShellCmd_GetCmdByIndex(WshShellCmd_Table_t* pShellCommands,
WshShell_Size_t idx) {
WSH_SHELL_ASSERT(pShellCommands);
if (!pShellCommands || !pShellCommands->List)
return NULL;
WSH_SHELL_ASSERT(idx < pShellCommands->Num);
return idx < pShellCommands->Num ? pShellCommands->List[idx] : NULL;
}
const WshShellCmd_t* WshShellCmd_SearchCmd(WshShellCmd_Table_t* pShellCommands,
const WshShell_Char_t* pcCmdName) {
WSH_SHELL_ASSERT(pcCmdName);
if (!pcCmdName)
return NULL;
for (WshShell_Size_t cmd = 0; cmd < WshShellCmd_GetCmdNum(pShellCommands); cmd++) {
const WshShellCmd_t* pcCmd = WshShellCmd_GetCmdByIndex(pShellCommands, cmd);
if (WSH_SHELL_STRNCMP(pcCmd->Name, pcCmdName, WSH_SHELL_CMD_NAME_LEN) == 0)
return pcCmd;
}
return NULL;
}
static const WshShellOption_t* WshShellCmd_FindOpt(const WshShellCmd_t* pcCmd,
const WshShell_Char_t* pcStr,
WshShell_Size_t strLen) {
WSH_SHELL_ASSERT(pcCmd && pcStr);
if (!pcCmd || !pcCmd)
return NULL;
const WshShellOption_t* pcWaitsInputOpt = NULL;
const WshShellOption_t* pcOpt = pcCmd->Options;
for (; pcOpt->Type != WSH_SHELL_OPTION_END; pcOpt++) {
switch (pcOpt->Type) {
case WSH_SHELL_OPTION_NO:
continue;
case WSH_SHELL_OPTION_WAITS_INPUT:
pcWaitsInputOpt = pcOpt;
continue;
default: {
const WshShell_Char_t* pRefStr = (strLen == WSH_SHELL_OPTION_SHORT_NAME_LEN)
? pcOpt->ShortName
: pcOpt->LongName;
WshShell_Size_t cmpLen = (strLen == WSH_SHELL_OPTION_SHORT_NAME_LEN)
? WSH_SHELL_OPTION_SHORT_NAME_LEN
: WSH_SHELL_OPTION_LONG_NAME_LEN;
if (WSH_SHELL_STRNCMP(pRefStr, pcStr, cmpLen) == 0)
return pcOpt;
}
}
}
return pcWaitsInputOpt;
}
WshShellOption_Ctx_t WshShellCmd_ParseOpt(const WshShellCmd_t* pcCmd, WshShell_Size_t argc,
const WshShell_Char_t* pArgv[], WshShell_Size_t rights,
WshShell_Size_t* pTokenPos) {
WSH_SHELL_ASSERT(pcCmd && pArgv && argc > 0 && pTokenPos);
WshShellOption_Ctx_t optCtx = {0};
if (!pcCmd || !pArgv || argc == 0 || !pTokenPos || *pTokenPos >= argc)
return optCtx;
if (argc == 1) { // Only command without options
const WshShellOption_t* pcOpt = pcCmd->Options;
for (; pcOpt->Type != WSH_SHELL_OPTION_END; pcOpt++) {
if (pcOpt->Type == WSH_SHELL_OPTION_NO) {
optCtx.Option = pcOpt;
break;
}
}
(*pTokenPos)++;
return optCtx;
}
if (*pTokenPos == 0) // Skip command token if not yet skipped
(*pTokenPos)++;
while (*pTokenPos < argc) {
const WshShell_Char_t* pcStr = pArgv[*pTokenPos];
WshShell_Size_t strLen = WSH_SHELL_STRLEN(pcStr);
const WshShellOption_t* pcOpt = WshShellCmd_FindOpt(pcCmd, pcStr, strLen);
if (!pcOpt) {
(*pTokenPos)++;
continue;
}
if (((pcOpt->Access & rights) == 0) && !(rights & WSH_SHELL_OPT_ACCESS_ADMIN)) {
WshShell_Char_t optRightsRow[5];
WshShell_Char_t usrRightsRow[5];
WshShellStr_AccessBitsToStr(pcOpt->Access, optRightsRow);
WshShellStr_AccessBitsToStr(rights, usrRightsRow);
WSH_SHELL_PRINT_ERR("No access rights for option \"%s\" (%s) and user (%s)\r\n", pcStr,
optRightsRow, usrRightsRow);
*pTokenPos += pcOpt->ArgNum;
(*pTokenPos)++;
continue;
}
optCtx.Option = pcOpt;
optCtx.TokenPos = *pTokenPos;
if (pcOpt->Type == WSH_SHELL_OPTION_WAITS_INPUT) {
// Consume all remaining arguments
*pTokenPos = argc;
} else {
*pTokenPos += pcOpt->ArgNum;
}
(*pTokenPos)++;
break;
}
return optCtx;
}
WSH_SHELL_RET_STATE_t WshShellCmd_GetOptValue(WshShellOption_Ctx_t* pOptCtx, WshShell_Size_t argc,
const WshShell_Char_t* pArgv[],
WshShell_Size_t valueSize, void* pValue) {
WSH_SHELL_ASSERT(pOptCtx && pOptCtx->Option && pArgv && pValue && valueSize);
if (!pOptCtx || !pOptCtx->Option || !pArgv || !pValue || valueSize == 0)
return WSH_SHELL_RET_STATE_ERR_PARAM;
if (argc < 2 || pOptCtx->TokenPos == 0)
return WSH_SHELL_RET_STATE_ERR_EMPTY;
WshShell_Size_t valIdx = pOptCtx->TokenPos + 1;
if (valIdx >= argc) {
WSH_SHELL_ASSERT(false);
return WSH_SHELL_RET_STATE_ERROR;
}
switch (pOptCtx->Option->Type) {
case WSH_SHELL_OPTION_STR:
WSH_SHELL_STRNCPY((WshShell_Char_t*)pValue, pArgv[valIdx], valueSize);
break;
case WSH_SHELL_OPTION_INT:
*((WshShell_Size_t*)pValue) = WSH_SHELL_STRTOL(pArgv[valIdx], NULL, 10);
break;
case WSH_SHELL_OPTION_FLOAT:
*((float*)pValue) = WSH_SHELL_STRTOF(pArgv[valIdx], NULL);
break;
case WSH_SHELL_OPTION_NO:
case WSH_SHELL_OPTION_HELP:
case WSH_SHELL_OPTION_WO_PARAM:
case WSH_SHELL_OPTION_MULTI_ARG:
case WSH_SHELL_OPTION_WAITS_INPUT:
case WSH_SHELL_OPTION_END:
case WSH_SHELL_OPTION_ENUM_SIZE:
default:
return WSH_SHELL_RET_STATE_ERR_EMPTY;
}
return WSH_SHELL_RET_STATE_SUCCESS;
}
void WshShellCmd_PrintOptionsOverview(const WshShellCmd_t* pcCmd) {
WSH_SHELL_ASSERT(pcCmd);
if (!pcCmd)
return;
WSH_SHELL_PRINT_SYS("%s\r\n", pcCmd->Descr);
#if WSH_SHELL_CMD_PRINT_OPT_OVERVIEW
WSH_SHELL_PRINT_SYS("Options overview:\r\n");
const WshShell_Size_t shortNameMaxLen = WSH_SHELL_OPTION_SHORT_NAME_LEN + 5;
const WshShell_Size_t longNameMaxLen = WSH_SHELL_OPTION_LONG_NAME_LEN;
const WshShell_Size_t typeMaxLen = 10;
const WshShell_Size_t accessMaxLen = 6;
WshShell_Char_t headTemplate[64];
WSH_SHELL_SNPRINTF(headTemplate, sizeof(headTemplate),
WSH_SHELL_COLOR_SYS
" %%-%ds %%-%ds %%-%ds %%-%ds %%s\r\n" WSH_SHELL_ESC_RESET_STYLE,
shortNameMaxLen, longNameMaxLen, typeMaxLen, accessMaxLen);
WSH_SHELL_PRINT(headTemplate, "Short", "Long", "Type", "Access", "Descr");
WshShell_Char_t rowTemplate[64];
WSH_SHELL_SNPRINTF(rowTemplate, sizeof(rowTemplate), " %%-%ds %%-%ds %%-%ds %%-%ds %%s\r\n",
shortNameMaxLen, longNameMaxLen, typeMaxLen, accessMaxLen);
const WshShellOption_t* pcOpt = pcCmd->Options;
for (; pcOpt->Type != WSH_SHELL_OPTION_END; pcOpt++) {
if (pcOpt->Type == WSH_SHELL_OPTION_WAITS_INPUT)
continue;
WshShell_Char_t accessRow[8];
WshShellStr_AccessBitsToStr(pcOpt->Access, accessRow);
WSH_SHELL_PRINT(rowTemplate, pcOpt->ShortName, pcOpt->LongName,
WshShell_OptTypeStr_Get(pcOpt->Type), accessRow, pcOpt->Descr);
}
#else /* WSH_SHELL_CMD_PRINT_OPT_OVERVIEW */
return;
#endif /* WSH_SHELL_CMD_PRINT_OPT_OVERVIEW */
}