// SoftEther VPN Source Code - Developer Edition Master Branch // Cedar Communication Module // Console.c // Console Service #include "CedarPch.h" // Display the help for the command void PrintCmdHelp(CONSOLE *c, char *cmd_name, TOKEN_LIST *param_list) { wchar_t tmp[MAX_SIZE]; wchar_t *buf; UINT buf_size; wchar_t *description, *args, *help; UNI_TOKEN_LIST *t; UINT width; UINT i; char *space; // Validate arguments if (c == NULL || cmd_name == NULL || param_list == NULL) { return; } width = GetConsoleWidth(c) - 2; buf_size = sizeof(wchar_t) * (width + 32); buf = Malloc(buf_size); GetCommandHelpStr(cmd_name, &description, &args, &help); space = MakeCharArray(' ', 2); // Title UniFormat(tmp, sizeof(tmp), _UU("CMD_HELP_TITLE"), cmd_name); c->Write(c, tmp); c->Write(c, L""); // Purpose c->Write(c, _UU("CMD_HELP_DESCRIPTION")); t = SeparateStringByWidth(description, width - 2); for (i = 0;i < t->NumTokens;i++) { UniFormat(buf, buf_size, L"%S%s", space, t->Token[i]); c->Write(c, buf); } UniFreeToken(t); c->Write(c, L""); // Description c->Write(c, _UU("CMD_HELP_HELP")); t = SeparateStringByWidth(help, width - 2); for (i = 0;i < t->NumTokens;i++) { UniFormat(buf, buf_size, L"%S%s", space, t->Token[i]); c->Write(c, buf); } UniFreeToken(t); c->Write(c, L""); // Usage c->Write(c, _UU("CMD_HELP_USAGE")); t = SeparateStringByWidth(args, width - 2); for (i = 0;i < t->NumTokens;i++) { UniFormat(buf, buf_size, L"%S%s", space, t->Token[i]); c->Write(c, buf); } UniFreeToken(t); // Arguments if (param_list->NumTokens >= 1) { c->Write(c, L""); c->Write(c, _UU("CMD_HELP_ARGS")); PrintCandidateHelp(c, cmd_name, param_list, 2); } Free(space); Free(buf); } // Evaluate whether it is SafeStr bool CmdEvalSafe(CONSOLE *c, wchar_t *str, void *param) { wchar_t *p = (param == NULL) ? _UU("CMD_EVAL_SAFE") : (wchar_t *)param; if (IsSafeUniStr(str)) { return true; } c->Write(c, p); return false; } // String input prompt wchar_t *CmdPrompt(CONSOLE *c, void *param) { wchar_t *p = (param == NULL) ? _UU("CMD_PROMPT") : (wchar_t *)param; return c->ReadLine(c, p, true); } // Evaluation whether the specified file exists bool CmdEvalIsFile(CONSOLE *c, wchar_t *str, void *param) { wchar_t tmp[MAX_PATH]; // Validate arguments if (c == NULL || str == NULL) { return false; } UniStrCpy(tmp, sizeof(tmp), str); if (IsEmptyUniStr(tmp)) { c->Write(c, _UU("CMD_FILE_NAME_EMPTY")); return false; } if (IsFileExistsW(tmp) == false) { wchar_t tmp2[MAX_SIZE]; UniFormat(tmp2, sizeof(tmp2), _UU("CMD_FILE_NOT_FOUND"), tmp); c->Write(c, tmp2); return false; } return true; } // Evaluation of integer bool CmdEvalInt1(CONSOLE *c, wchar_t *str, void *param) { wchar_t *p = (param == NULL) ? _UU("CMD_EVAL_INT") : (wchar_t *)param; if (UniToInt(str) == 0) { c->Write(c, p); return false; } return true; } // Evaluation of the parameters that a blank cannot be specified to bool CmdEvalNotEmpty(CONSOLE *c, wchar_t *str, void *param) { wchar_t *p = (param == NULL) ? _UU("CMD_EVAL_NOT_EMPTY") : (wchar_t *)param; if (UniIsEmptyStr(str) == false) { return true; } c->Write(c, p); return false; } // Evaluation function for minimum / maximum value of the parameter bool CmdEvalMinMax(CONSOLE *c, wchar_t *str, void *param) { CMD_EVAL_MIN_MAX *e; wchar_t *tag; UINT v; // Validate arguments if (param == NULL) { return false; } e = (CMD_EVAL_MIN_MAX *)param; if (e->StrName == NULL) { tag = _UU("CMD_EVAL_MIN_MAX"); } else { tag = _UU(e->StrName); } v = UniToInt(str); if (v >= e->MinValue && v <= e->MaxValue) { return true; } else { wchar_t tmp[MAX_SIZE]; UniFormat(tmp, sizeof(tmp), tag, e->MinValue, e->MaxValue); c->Write(c, tmp); return false; } } // Get the help string of command void GetCommandHelpStr(char *command_name, wchar_t **description, wchar_t **args, wchar_t **help) { char tmp1[128], tmp2[128], tmp3[128]; Format(tmp1, sizeof(tmp1), "CMD_%s", command_name); Format(tmp2, sizeof(tmp2), "CMD_%s_ARGS", command_name); Format(tmp3, sizeof(tmp3), "CMD_%s_HELP", command_name); if (description != NULL) { *description = _UU(tmp1); if (UniIsEmptyStr(*description)) { *description = _UU("CMD_UNKNOWM"); } } if (args != NULL) { *args = _UU(tmp2); if (UniIsEmptyStr(*args)) { *args = _UU("CMD_UNKNOWN_ARGS"); } } if (help != NULL) { *help = _UU(tmp3); if (UniIsEmptyStr(*help)) { *help = _UU("CMD_UNKNOWN_HELP"); } } } // Get the help string for parameter void GetCommandParamHelpStr(char *command_name, char *param_name, wchar_t **description) { char tmp[160]; if (description == NULL) { return; } Format(tmp, sizeof(tmp), "CMD_%s_%s", command_name, param_name); *description = _UU(tmp); if (UniIsEmptyStr(*description)) { *description = _UU("CMD_UNKNOWN_PARAM"); } } // String comparison function int CompareCandidateStr(void *p1, void *p2) { char *s1, *s2; if (p1 == NULL || p2 == NULL) { return 0; } s1 = *(char **)p1; s2 = *(char **)p2; if (s1 == NULL || s2 == NULL) { return 0; } if (s1[0] == '[' && s2[0] != '[') { return -1; } else if (s2[0] == '[' && s1[0] != '[') { return 1; } return StrCmp(s1, s2); } // Display the help of the candidate list void PrintCandidateHelp(CONSOLE *c, char *cmd_name, TOKEN_LIST *candidate_list, UINT left_space) { UINT console_width; UINT max_keyword_width; LIST *o; UINT i; wchar_t *tmpbuf; UINT tmpbuf_size; char *left_space_array; char *max_space_array; // Validate arguments if (c == NULL || candidate_list == NULL) { return; } // Get the width of the screen console_width = GetConsoleWidth(c) - 1; tmpbuf_size = sizeof(wchar_t) * (console_width + 32); tmpbuf = Malloc(tmpbuf_size); left_space_array = MakeCharArray(' ', left_space); // Sort and enlist the command name // no need to sort the parameter name o = NewListFast(cmd_name == NULL ? CompareCandidateStr : NULL); max_keyword_width = 0; for (i = 0;i < candidate_list->NumTokens;i++) { UINT keyword_width; // Get the width of each keyword Insert(o, candidate_list->Token[i]); keyword_width = StrWidth(candidate_list->Token[i]); if (cmd_name != NULL) { if (candidate_list->Token[i][0] != '[') { keyword_width += 1; } else { keyword_width -= 2; } } max_keyword_width = MAX(max_keyword_width, keyword_width); } max_space_array = MakeCharArray(' ', max_keyword_width); // Display the candidate for (i = 0;i < LIST_NUM(o);i++) { char tmp[128]; char *name = LIST_DATA(o, i); UNI_TOKEN_LIST *t; wchar_t *help; UINT j; UINT keyword_start_width = left_space; UINT descript_start_width = left_space + max_keyword_width + 1; UINT descript_width; char *space; if (console_width >= (descript_start_width + 5)) { descript_width = console_width - descript_start_width - 3; } else { descript_width = 2; } // Generate the name if (cmd_name != NULL && name[0] != '[') { // Prepend a "/" in the case of a parameter Format(tmp, sizeof(tmp), "/%s", name); } else { // Use the characters as it is in the case of a command name if (cmd_name == NULL) { StrCpy(tmp, sizeof(tmp), name); } else { StrCpy(tmp, sizeof(tmp), name + 1); if (StrLen(tmp) >= 1) { tmp[StrLen(tmp) - 1] = 0; } } } // Get the help string if (cmd_name == NULL) { GetCommandHelpStr(name, &help, NULL, NULL); } else { GetCommandParamHelpStr(cmd_name, name, &help); } space = MakeCharArray(' ', max_keyword_width - StrWidth(name) - (cmd_name == NULL ? 0 : (name[0] != '[' ? 1 : -2))); t = SeparateStringByWidth(help, descript_width); for (j = 0;j < t->NumTokens;j++) { if (j == 0) { UniFormat(tmpbuf, tmpbuf_size, L"%S%S%S - %s", left_space_array, tmp, space, t->Token[j]); } else { UniFormat(tmpbuf, tmpbuf_size, L"%S%S %s", left_space_array, max_space_array, t->Token[j]); } c->Write(c, tmpbuf); } Free(space); UniFreeToken(t); } ReleaseList(o); Free(max_space_array); Free(tmpbuf); Free(left_space_array); } // Acquisition whether word characters bool IsWordChar(wchar_t c) { if (c >= L'a' && c <= 'z') { return true; } if (c >= L'A' && c <= 'Z') { return true; } if (c >= L'0' && c <= '9') { return true; } if (c == L'_') { return true; } if (c == L'.') { return true; } if (c == L'\"') { return true; } if (c == L'\'') { return true; } if (c == L',') { return true; } if (c == L')') { return true; } if (c == L']') { return true; } return false; } // Get the character width of the word that comes next UINT GetNextWordWidth(wchar_t *str) { UINT i; UINT ret; // Validate arguments if (str == NULL) { return 0; } ret = 0; for (i = 0;;i++) { wchar_t c = str[i]; if (c == 0) { break; } if (IsWordChar(c) == false) { break; } ret++; } return ret; } // Split a string into specified width UNI_TOKEN_LIST *SeparateStringByWidth(wchar_t *str, UINT width) { UINT wp; wchar_t *tmp; UINT len, i; LIST *o; UNI_TOKEN_LIST *ret; // Validate arguments if (str == NULL) { return UniNullToken(); } if (width == 0) { width = 1; } o = NewListFast(NULL); len = UniStrLen(str); tmp = ZeroMalloc(sizeof(wchar_t) * (len + 32)); wp = 0; for (i = 0;i < (len + 1);i++) { wchar_t c = str[i]; UINT next_word_width; UINT remain_width; switch (c) { case 0: case L'\r': case L'\n': if (c == L'\r') { if (str[i + 1] == L'\n') { i++; } } tmp[wp++] = 0; wp = 0; Insert(o, UniCopyStr(tmp)); break; default: next_word_width = GetNextWordWidth(&str[i]); remain_width = (width - UniStrWidth(tmp)); if ((remain_width >= 1) && (next_word_width > remain_width) && (next_word_width <= width)) { tmp[wp++] = 0; wp = 0; Insert(o, UniCopyStr(tmp)); } tmp[wp++] = c; tmp[wp] = 0; if (UniStrWidth(tmp) >= width) { tmp[wp++] = 0; wp = 0; Insert(o, UniCopyStr(tmp)); } break; } } if (LIST_NUM(o) == 0) { Insert(o, CopyUniStr(L"")); } ret = ZeroMalloc(sizeof(UNI_TOKEN_LIST)); ret->NumTokens = LIST_NUM(o); ret->Token = ZeroMalloc(sizeof(wchar_t *) * ret->NumTokens); for (i = 0;i < LIST_NUM(o);i++) { wchar_t *s = LIST_DATA(o, i); UniTrimLeft(s); ret->Token[i] = s; } ReleaseList(o); Free(tmp); return ret; } // Check whether the specified string means 'help' bool IsHelpStr(char *str) { // Validate arguments if (str == NULL) { return false; } if (StrCmpi(str, "help") == 0 || StrCmpi(str, "?") == 0 || StrCmpi(str, "man") == 0 || StrCmpi(str, "/man") == 0 || StrCmpi(str, "-man") == 0 || StrCmpi(str, "--man") == 0 || StrCmpi(str, "/help") == 0 || StrCmpi(str, "/?") == 0 || StrCmpi(str, "-help") == 0 || StrCmpi(str, "-?") == 0 || StrCmpi(str, "/h") == 0 || StrCmpi(str, "--help") == 0 || StrCmpi(str, "--?") == 0) { return true; } return false; } // Execution of the command bool DispatchNextCmd(CONSOLE *c, char *prompt, CMD cmd[], UINT num_cmd, void *param) { return DispatchNextCmdEx(c, NULL, prompt, cmd, num_cmd, param); } bool DispatchNextCmdEx(CONSOLE *c, wchar_t *exec_command, char *prompt, CMD cmd[], UINT num_cmd, void *param) { wchar_t *str; wchar_t *tmp; char *cmd_name; bool b_exit = false; wchar_t *cmd_param; UINT ret = ERR_NO_ERROR; TOKEN_LIST *t; TOKEN_LIST *candidate; bool no_end_crlf = false; UINT i; // Validate arguments if (c == NULL || (num_cmd >= 1 && cmd == NULL)) { return false; } if (exec_command == NULL) { // Show the prompt RETRY: tmp = CopyStrToUni(prompt); if (c->ProgrammingMode) { wchar_t tmp2[MAX_PATH]; UniFormat(tmp2, sizeof(tmp2), L"[PROMPT:%u:%s]\r\n", c->RetCode, tmp); Free(tmp); tmp = CopyUniStr(tmp2); } str = c->ReadLine(c, tmp, false); Free(tmp); if (str != NULL && IsEmptyUniStr(str)) { Free(str); goto RETRY; } } else { wchar_t tmp[MAX_SIZE]; // Use exec_command if (UniStartWith(exec_command, L"vpncmd") == false) { if (prompt != NULL) { if (c->ConsoleType != CONSOLE_CSV) { UniFormat(tmp, sizeof(tmp), L"%S%s", prompt, exec_command); c->Write(c, tmp); } } } str = CopyUniStr(exec_command); } if (str == NULL) { // User canceled return false; } UniTrimCrlf(str); UniTrim(str); if (UniIsEmptyStr(str)) { // Do Nothing Free(str); return true; } // Divide into command name and parameter if (SeparateCommandAndParam(str, &cmd_name, &cmd_param) == false) { // Do Nothing Free(str); return true; } if (StrLen(cmd_name) >= 2 && cmd_name[0] == '?' && cmd_name[1] != '?') { char tmp[MAX_SIZE]; wchar_t *s; StrCpy(tmp, sizeof(tmp), cmd_name + 1); StrCpy(cmd_name, 0, tmp); s = UniCopyStr(L"/?"); Free(cmd_param); cmd_param = s; } if (StrLen(cmd_name) >= 2 && EndWith(cmd_name, "?") && cmd_name[StrLen(cmd_name) - 2] != '?') { wchar_t *s; cmd_name[StrLen(cmd_name) - 1] = 0; s = UniCopyStr(L"/?"); Free(cmd_param); cmd_param = s; } // Get the candidate of command t = ZeroMalloc(sizeof(TOKEN_LIST)); t->NumTokens = num_cmd; t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens); for (i = 0;i < t->NumTokens;i++) { t->Token[i] = CopyStr(cmd[i].Name); } if (IsHelpStr(cmd_name)) { if (UniIsEmptyStr(cmd_param)) { wchar_t tmp[MAX_SIZE]; // Display the list of commands that can be used UniFormat(tmp, sizeof(tmp), _UU("CMD_HELP_1"), t->NumTokens); c->Write(c, tmp); PrintCandidateHelp(c, NULL, t, 1); c->Write(c, L""); c->Write(c, _UU("CMD_HELP_2")); } else { char *cmd_name; // Display the help for the specified command if (SeparateCommandAndParam(cmd_param, &cmd_name, NULL)) { bool b = true; if (IsHelpStr(cmd_name)) { b = false; } if (b) { wchar_t str[MAX_SIZE]; UniFormat(str, sizeof(str), L"%S /help", cmd_name); DispatchNextCmdEx(c, str, NULL, cmd, num_cmd, param); no_end_crlf = true; } Free(cmd_name); } } } else if (StrCmpi(cmd_name, "exit") == 0 || StrCmpi(cmd_name, "quit") == 0) { // Exit b_exit = true; } else { candidate = GetRealnameCandidate(cmd_name, t); if (candidate == NULL || candidate->NumTokens == 0) { wchar_t tmp[MAX_SIZE]; // No candidate UniFormat(tmp, sizeof(tmp), _UU("CON_UNKNOWN_CMD"), cmd_name); c->Write(c, tmp); c->RetCode = ERR_BAD_COMMAND_OR_PARAM; } else if (candidate->NumTokens >= 2) { wchar_t tmp[MAX_SIZE]; // There is more than one candidate UniFormat(tmp, sizeof(tmp), _UU("CON_AMBIGUOUS_CMD"), cmd_name); c->Write(c, tmp); c->Write(c, _UU("CON_AMBIGUOUS_CMD_1")); PrintCandidateHelp(c, NULL, candidate, 1); c->Write(c, _UU("CON_AMBIGUOUS_CMD_2")); c->RetCode = ERR_BAD_COMMAND_OR_PARAM; } else { char *real_cmd_name; UINT i; // The candidate was shortlisted to one real_cmd_name = candidate->Token[0]; for (i = 0;i < num_cmd;i++) { if (StrCmpi(cmd[i].Name, real_cmd_name) == 0) { if (cmd[i].Proc != NULL) { // Show the description of the command if it isn't in CSV mode if(c->ConsoleType != CONSOLE_CSV) { wchar_t tmp[256]; wchar_t *note; GetCommandHelpStr(cmd[i].Name, ¬e, NULL, NULL); UniFormat(tmp, sizeof(tmp), _UU("CMD_EXEC_MSG_NAME"), cmd[i].Name, note); c->Write(c, tmp); } // Call the procedure of the command ret = cmd[i].Proc(c, cmd[i].Name, cmd_param, param); if (ret == INFINITE) { // Exit command b_exit = true; } else { c->RetCode = ret; } } } } } FreeToken(candidate); } FreeToken(t); Free(str); Free(cmd_name); Free(cmd_param); if (no_end_crlf == false) { //c->Write(c, L""); } if (b_exit) { return false; } return true; } // Get the width of the current console UINT GetConsoleWidth(CONSOLE *c) { UINT size; size = c->GetWidth(c); if (size == 0) { size = 80; } if (size < 32) { size = 32; } if (size > 65536) { size = 65535; } return size; } // Separate the command line into the command and the parameters bool SeparateCommandAndParam(wchar_t *src, char **cmd, wchar_t **param) { UINT i, len, wp; wchar_t *tmp; wchar_t *src_tmp; // Validate arguments if (src == NULL) { return false; } if (cmd != NULL) { *cmd = NULL; } if (param != NULL) { *param = NULL; } src_tmp = UniCopyStr(src); UniTrimCrlf(src_tmp); UniTrim(src_tmp); len = UniStrLen(src_tmp); tmp = Malloc(sizeof(wchar_t) * (len + 32)); wp = 0; for (i = 0;i < (len + 1);i++) { wchar_t c = src_tmp[i]; switch (c) { case 0: case L' ': case L'\t': tmp[wp] = 0; if (UniIsEmptyStr(tmp)) { Free(tmp); Free(src_tmp); return false; } if (cmd != NULL) { *cmd = CopyUniToStr(tmp); Trim(*cmd); } goto ESCAPE; default: tmp[wp++] = c; break; } } ESCAPE: if (param != NULL) { *param = CopyUniStr(&src_tmp[wp]); UniTrim(*param); } Free(tmp); Free(src_tmp); return true; } // Get the candidates list of of the real command name whose abbreviation matches to the command specified by the user TOKEN_LIST *GetRealnameCandidate(char *input_name, TOKEN_LIST *real_name_list) { TOKEN_LIST *ret; LIST *o; UINT i; bool ok = false; // Validate arguments if (input_name == NULL || real_name_list == NULL) { return NullToken(); } o = NewListFast(NULL); for (i = 0;i < real_name_list->NumTokens;i++) { char *name = real_name_list->Token[i]; // Search for an exact match with the highest priority first if (StrCmpi(name, input_name) == 0) { Insert(o, name); ok = true; break; } } if (ok == false) { // If there is no command to exact match, check whether it matches to a short form command for (i = 0;i < real_name_list->NumTokens;i++) { char *name = real_name_list->Token[i]; if (IsOmissionName(input_name, name) || IsNameInRealName(input_name, name)) { // A abbreviation is found Insert(o, name); ok = true; } } } if (ok) { // One or more candidate is found ret = ListToTokenList(o); } else { ret = NullToken(); } ReleaseList(o); return ret; } // Check whether the command specified by the user is a abbreviation of existing commands bool IsOmissionName(char *input_name, char *real_name) { char oname[128]; // Validate arguments if (input_name == NULL || real_name == NULL) { return false; } if (IsAllUpperStr(real_name)) { // Command of all capital letters do not take abbreviations return false; } GetOmissionName(oname, sizeof(oname), real_name); if (IsEmptyStr(oname)) { return false; } if (StartWith(oname, input_name)) { // Example: The oname of AccountSecureCertSet is "ascs". // But if the user enters "asc", returns true return true; } if (StartWith(input_name, oname)) { // Example: When two commands AccountCreate and AccountConnect exist, // if the user enter "aconnect" , only AccountConnect is true if (EndWith(real_name, &input_name[StrLen(oname)])) { return true; } } return false; } // Get the short name of the specified command void GetOmissionName(char *dst, UINT size, char *src) { UINT i, len; // Validate arguments if (dst == NULL || src == NULL) { return; } StrCpy(dst, size, ""); len = StrLen(src); for (i = 0;i < len;i++) { char c = src[i]; if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')) { char tmp[2]; tmp[0] = c; tmp[1] = 0; StrCat(dst, size, tmp); } } } // Check whether the command specified by the user matches the existing commands bool IsNameInRealName(char *input_name, char *real_name) { // Validate arguments if (input_name == NULL || real_name == NULL) { return false; } if (StartWith(real_name, input_name)) { return true; } return false; } // Parse the command list LIST *ParseCommandList(CONSOLE *c, char *cmd_name, wchar_t *command, PARAM param[], UINT num_param) { UINT i; LIST *o; bool ok = true; TOKEN_LIST *param_list; TOKEN_LIST *real_name_list; bool help_mode = false; wchar_t *tmp; // Validate arguments if (c == NULL || command == NULL || (num_param >= 1 && param == NULL) || cmd_name == NULL) { return NULL; } // Initialization for (i = 0;i < num_param;i++) { if (IsEmptyStr(param[i].Name) == false) { if (param[i].Name[0] == '[') { param[i].Tmp = ""; } else { param[i].Tmp = NULL; } } else { param[i].Tmp = ""; } } real_name_list = ZeroMalloc(sizeof(TOKEN_LIST)); real_name_list->NumTokens = num_param; real_name_list->Token = ZeroMalloc(sizeof(char *) * real_name_list->NumTokens); for (i = 0;i < real_name_list->NumTokens;i++) { real_name_list->Token[i] = CopyStr(param[i].Name); } // Generate a list of parameter name specified by the user param_list = GetCommandNameList(command); for (i = 0;i < param_list->NumTokens;i++) { char *s = param_list->Token[i]; if (StrCmpi(s, "help") == 0 || StrCmpi(s, "?") == 0) { help_mode = true; break; } } tmp = ParseCommand(command, L""); if (tmp != NULL) { if (UniStrCmpi(tmp, L"?") == 0) { help_mode = true; } Free(tmp); } if (help_mode) { // Show the help PrintCmdHelp(c, cmd_name, real_name_list); FreeToken(param_list); FreeToken(real_name_list); return NULL; } for (i = 0;i < param_list->NumTokens;i++) { // Get the corresponding commands for all parameter names which is specified by the user TOKEN_LIST *candidate = GetRealnameCandidate(param_list->Token[i], real_name_list); if (candidate != NULL && candidate->NumTokens >= 1) { if (candidate->NumTokens >= 2) { wchar_t tmp[MAX_SIZE]; // There is more than one candidate UniFormat(tmp, sizeof(tmp), _UU("CON_AMBIGUOUS_PARAM"), param_list->Token[i]); c->Write(c, tmp); UniFormat(tmp, sizeof(tmp), _UU("CON_AMBIGUOUS_PARAM_1"), cmd_name); c->Write(c, tmp); PrintCandidateHelp(c, cmd_name, candidate, 1); c->Write(c, _UU("CON_AMBIGUOUS_PARAM_2")); ok = false; } else { UINT j; char *real_name = candidate->Token[0]; // There is only one candidate for (j = 0;j < num_param;j++) { if (StrCmpi(param[j].Name, real_name) == 0) { param[j].Tmp = param_list->Token[i]; } } } } else { wchar_t tmp[MAX_SIZE]; // No candidate UniFormat(tmp, sizeof(tmp), _UU("CON_INVALID_PARAM"), param_list->Token[i], cmd_name, cmd_name); c->Write(c, tmp); ok = false; } FreeToken(candidate); } if (ok == false) { FreeToken(param_list); FreeToken(real_name_list); return NULL; } // Creating a list o = NewParamValueList(); // Read all the parameters of the specified name in the parameter list for (i = 0;i < num_param;i++) { bool prompt_input_value = false; PARAM *p = ¶m[i]; if (p->Tmp != NULL || p->PromptProc != NULL) { wchar_t *name = CopyStrToUni(p->Name); wchar_t *tmp; wchar_t *str; if (p->Tmp != NULL) { tmp = CopyStrToUni(p->Tmp); } else { tmp = CopyStrToUni(p->Name); } str = ParseCommand(command, tmp); Free(tmp); if (str != NULL) { wchar_t *unistr; bool ret; EVAL_VALUE: // Reading succeeded unistr = str; if (p->EvalProc != NULL) { // Evaluate the value if EvalProc is specified ret = p->EvalProc(c, unistr, p->EvalProcParam); } else { // Accept any value if EvalProc is not specified ret = true; } if (ret == false) { // The specified value is invalid if (p->PromptProc == NULL) { // Cancel ok = false; Free(name); Free(str); break; } else if (c->ProgrammingMode) { // In the programming mode, return the error immediately. ok = false; Free(name); Free(str); break; } else { // Request to re-enter Free(str); str = NULL; goto SHOW_PROMPT; } } else { PARAM_VALUE *v; // Finished loading, add it to the list v = ZeroMalloc(sizeof(PARAM_VALUE)); v->Name = CopyStr(p->Name); v->StrValue = CopyUniToStr(str); v->UniStrValue = CopyUniStr(str); v->IntValue = ToInt(v->StrValue); Insert(o, v); } } else { // Failed to read. The parameter is not specified if (p->PromptProc != NULL) { wchar_t *tmp; SHOW_PROMPT: // Prompt because it is a mandatory parameter tmp = NULL; if (c->ProgrammingMode == false) { tmp = p->PromptProc(c, p->PromptProcParam); } if (tmp == NULL) { // User canceled ok = false; Free(str); Free(name); break; } else { // Entered by the user c->Write(c, L""); str = tmp; prompt_input_value = true; goto EVAL_VALUE; } } } Free(str); Free(name); } } FreeToken(param_list); FreeToken(real_name_list); if (ok) { return o; } else { FreeParamValueList(o); return NULL; } } // Acquisition of [Yes] or [No] bool GetParamYes(LIST *o, char *name) { char *s; char tmp[64]; // Validate arguments if (o == NULL) { return false; } s = GetParamStr(o, name); if (s == NULL) { return false; } StrCpy(tmp, sizeof(tmp), s); Trim(tmp); if (StartWith(tmp, "y")) { return true; } if (StartWith(tmp, "t")) { return true; } if (ToInt(tmp) != 0) { return true; } return false; } // Acquisition of parameter value Int UINT GetParamInt(LIST *o, char *name) { PARAM_VALUE *v; // Validate arguments if (o == NULL) { return 0; } v = FindParamValue(o, name); if (v == NULL) { return 0; } else { return v->IntValue; } } // Acquisition of parameter value Unicode string wchar_t *GetParamUniStr(LIST *o, char *name) { PARAM_VALUE *v; // Validate arguments if (o == NULL) { return NULL; } v = FindParamValue(o, name); if (v == NULL) { return NULL; } else { return v->UniStrValue; } } // Acquisition of the parameter value string char *GetParamStr(LIST *o, char *name) { PARAM_VALUE *v; // Validate arguments if (o == NULL) { return NULL; } v = FindParamValue(o, name); if (v == NULL) { return NULL; } else { return v->StrValue; } } // Acquisition of parameter value PARAM_VALUE *FindParamValue(LIST *o, char *name) { PARAM_VALUE t, *ret; // Validate arguments if (o == NULL) { return NULL; } if (name == NULL) { name = ""; } Zero(&t, sizeof(t)); t.Name = name; ret = Search(o, &t); return ret; } // Release of the parameter value list void FreeParamValueList(LIST *o) { UINT i; // Validate arguments if (o == NULL) { return; } for (i = 0;i < LIST_NUM(o);i++) { PARAM_VALUE *v = LIST_DATA(o, i); Free(v->StrValue); Free(v->UniStrValue); Free(v->Name); Free(v); } ReleaseList(o); } // Parameter value list sort function int CmpParamValue(void *p1, void *p2) { PARAM_VALUE *v1, *v2; if (p1 == NULL || p2 == NULL) { return 0; } v1 = *(PARAM_VALUE **)p1; v2 = *(PARAM_VALUE **)p2; if (v1 == NULL || v2 == NULL) { return 0; } if (IsEmptyStr(v1->Name) && IsEmptyStr(v2->Name)) { return 0; } return StrCmpi(v1->Name, v2->Name); } // Generation of the parameter value list LIST *NewParamValueList() { return NewListFast(CmpParamValue); } // Get the list of parameter names that were included in the entered command TOKEN_LIST *GetCommandNameList(wchar_t *str) { TOKEN_LIST *t; // Validate arguments if (str == NULL) { return NullToken(); } Free(ParseCommandEx(str, L"dummy_str", &t)); return t; } // Get the commands that start with the specified name wchar_t *ParseCommand(wchar_t *str, wchar_t *name) { return ParseCommandEx(str, name, NULL); } wchar_t *ParseCommandEx(wchar_t *str, wchar_t *name, TOKEN_LIST **param_list) { UNI_TOKEN_LIST *t; UINT i; wchar_t *tmp; wchar_t *ret = NULL; LIST *o; // Validate arguments if (str == NULL) { return NULL; } if (name != NULL && UniIsEmptyStr(name)) { name = NULL; } o = NULL; if (param_list != NULL) { o = NewListFast(CompareStr); } tmp = CopyUniStr(str); UniTrim(tmp); i = UniSearchStrEx(tmp, L"/CMD ", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/') { i = INFINITE; } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"/CMD\t", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/') { i = INFINITE; } } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"/CMD:", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/') { i = INFINITE; } } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"/CMD=", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'/') { i = INFINITE; } } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"-CMD ", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-') { i = INFINITE; } } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"-CMD\t", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-') { i = INFINITE; } } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"-CMD:", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-') { i = INFINITE; } } if (i == INFINITE) { i = UniSearchStrEx(tmp, L"-CMD=", 0, false); if (i != INFINITE && i >= 1 && tmp[i - 1] == L'-') { i = INFINITE; } } if (i != INFINITE) { char *s = CopyStr("CMD"); if (InsertStr(o, s) == false) { Free(s); } if (UniStrCmpi(name, L"CMD") == 0) { ret = CopyUniStr(&str[i + 5]); UniTrim(ret); } else { tmp[i] = 0; } } if (ret == NULL) { t = UniParseCmdLine(tmp); if (t != NULL) { for (i = 0;i < t->NumTokens;i++) { wchar_t *token = t->Token[i]; if ((token[0] == L'-' && token[1] != L'-') || (UniStrCmpi(token, L"--help") == 0) || (token[0] == L'/' && token[1] != L'/')) { UINT i; // Named parameter // Examine whether there is a colon character if (UniStrCmpi(token, L"--help") == 0) { token++; } i = UniSearchStrEx(token, L":", 0, false); if (i == INFINITE) { i = UniSearchStrEx(token, L"=", 0, false); } if (i != INFINITE) { wchar_t *tmp; char *a; // There is a colon character tmp = CopyUniStr(token); tmp[i] = 0; a = CopyUniToStr(&tmp[1]); if (InsertStr(o, a) == false) { Free(a); } if (UniStrCmpi(name, &tmp[1]) == 0) { if (ret == NULL) { // Content ret = UniCopyStr(&token[i + 1]); } } Free(tmp); } else { // There is no colon character char *a; a = CopyUniToStr(&token[1]); if (InsertStr(o, a) == false) { Free(a); } if (UniStrCmpi(name, &token[1]) == 0) { if (ret == NULL) { // Empty character ret = UniCopyStr(L""); } } } } else { // Nameless argument if (name == NULL) { if (ret == NULL) { if (token[0] == L'-' && token[1] == L'-') { ret = UniCopyStr(&token[1]); } else if (token[0] == L'/' && token[1] == L'/') { ret = UniCopyStr(&token[1]); } else { ret = UniCopyStr(token); } } } } } UniFreeToken(t); } } Free(tmp); if (o != NULL) { TOKEN_LIST *t = ZeroMalloc(sizeof(TOKEN_LIST)); UINT i; t->NumTokens = LIST_NUM(o); t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens); for (i = 0;i < t->NumTokens;i++) { t->Token[i] = LIST_DATA(o, i); } ReleaseList(o); *param_list = t; } if (UniStrCmpi(ret, L"none") == 0 || UniStrCmpi(ret, L"null") == 0) { // Null and none are reserved words ret[0] = 0; } return ret; } char *ParseCommandA(wchar_t *str, char *name) { wchar_t *tmp1, *tmp2; char *ret; // Validate arguments if (str == NULL) { return NULL; } if (name != NULL) { tmp1 = CopyStrToUni(name); } else { tmp1 = NULL; } tmp2 = ParseCommand(str, tmp1); if (tmp2 == NULL) { ret = NULL; } else { ret = CopyUniToStr(tmp2); Free(tmp2); } Free(tmp1); return ret; } // Password prompt bool PasswordPrompt(char *password, UINT size) { UINT wp; bool escape = false; void *console; // Validate arguments if (password == NULL || size <= 1) { if (size >= 1) { password[0] = 0; } return false; } wp = 0; Zero(password, size); console = SetConsoleRaw(); while (true) { int c; #ifdef OS_WIN32 c = getch(); #else // OS_WIN32 c = getc(stdin); #endif // OS_WIN32 if (c >= 0x20 && c <= 0x7E) { // Character if ((wp + 1) < size) { password[wp++] = (char)c; putc('*', stdout); } } else if (c == 0x03) { // Break exit(0); } else if (c == 0x04 || c == 0x1a || c == 0x0D || c==0x0A) { // Exit if (c == 0x04 || c == 0x1a) { escape = true; } break; } else if (c == 0xE0) { // Read one more character #ifdef OS_WIN32 c = getch(); #else // OS_WIN32 c = getc(stdin); #endif // OS_WIN32 if (c == 0x4B || c == 0x53) { // Backspace goto BACKSPACE; } } else if (c == 0x08) { BACKSPACE: // Backspace if (wp >= 1) { password[--wp] = 0; putc(0x08, stdout); putc(' ', stdout); putc(0x08, stdout); } } } Print("\n"); RestoreConsole(console); return (escape ? false : true); } // Show the prompt wchar_t *Prompt(wchar_t *prompt_str) { wchar_t *ret = NULL; wchar_t *tmp = NULL; // Validate arguments if (prompt_str == NULL) { prompt_str = L""; } #ifdef OS_WIN32 UniPrint(L"%s", prompt_str); tmp = Malloc(MAX_PROMPT_STRSIZE); if (fgetws(tmp, MAX_PROMPT_STRSIZE - 1, stdin) != NULL) { bool escape = false; UINT i, len; len = UniStrLen(tmp); for (i = 0;i < len;i++) { if (tmp[i] == 0x04 || tmp[i] == 0x1A) { escape = true; break; } } if (escape == false) { UniTrimCrlf(tmp); ret = UniCopyStr(tmp); } } Free(tmp); #else // OS_WIN32 { char *prompt = CopyUniToStr(prompt_str); char *s = readline(prompt); Free(prompt); if (s != NULL) { TrimCrlf(s); Trim(s); if (IsEmptyStr(s) == false) { add_history(s); } ret = CopyStrToUni(s); free(s); } } #endif // OS_WIN32 if (ret == NULL) { Print("\n"); } return ret; } char *PromptA(wchar_t *prompt_str) { wchar_t *str = Prompt(prompt_str); if (str == NULL) { return NULL; } else { char *ret = CopyUniToStr(str); Free(str); return ret; } } // Set the console to raw mode void *SetConsoleRaw() { #ifdef OS_UNIX struct termios t, *ret; Zero(&t, sizeof(t)); if (tcgetattr(0, &t) != 0) { // Failed return NULL; } // Copy the current settings ret = Clone(&t, sizeof(t)); // Change the settings t.c_lflag &= (~ICANON); t.c_lflag &= (~ECHO); t.c_cc[VTIME] = 0; t.c_cc[VMIN] = 1; tcsetattr(0, TCSANOW, &t); return ret; #else // OS_UNIX return Malloc(0); #endif // OS_UNIX } // Restore the mode of the console void RestoreConsole(void *p) { #ifdef OS_UNIX struct termios *t; // Validate arguments if (p == NULL) { return; } t = (struct termios *)p; // Restore the settings tcsetattr(0, TCSANOW, t); Free(t); #else // OS_UNIX if (p != NULL) { Free(p); } #endif // OS_UNIX } //////////////////////////// // Local console function // Creating a new local console CONSOLE *NewLocalConsole(wchar_t *infile, wchar_t *outfile) { IO *in_io = NULL, *out_io = NULL; CONSOLE *c = ZeroMalloc(sizeof(CONSOLE)); LOCAL_CONSOLE_PARAM *p; UINT old_size = 0; #ifdef OS_WIN32 if (MsGetConsoleWidth() == 80) { //old_size = MsSetConsoleWidth(WIN32_DEFAULT_CONSOLE_WIDTH); } #endif // OS_WIN32 c->ConsoleType = CONSOLE_LOCAL; c->Free = ConsoleLocalFree; c->ReadLine = ConsoleLocalReadLine; c->ReadPassword = ConsoleLocalReadPassword; c->Write = ConsoleLocalWrite; c->GetWidth = ConsoleLocalGetWidth; c->OutputLock = NewLock(); if (UniIsEmptyStr(infile) == false) { // Input file is specified in_io = FileOpenW(infile, false); if (in_io == NULL) { wchar_t tmp[MAX_SIZE]; UniFormat(tmp, sizeof(tmp), _UU("CON_INFILE_ERROR"), infile); c->Write(c, tmp); Free(c); return NULL; } else { wchar_t tmp[MAX_SIZE]; UniFormat(tmp, sizeof(tmp), _UU("CON_INFILE_START"), infile); c->Write(c, tmp); } } if (UniIsEmptyStr(outfile) == false) { // Output file is specified out_io = FileCreateW(outfile); if (out_io == NULL) { wchar_t tmp[MAX_SIZE]; UniFormat(tmp, sizeof(tmp), _UU("CON_OUTFILE_ERROR"), outfile); c->Write(c, tmp); Free(c); if (in_io != NULL) { FileClose(in_io); } return NULL; } else { wchar_t tmp[MAX_SIZE]; UniFormat(tmp, sizeof(tmp), _UU("CON_OUTFILE_START"), outfile); c->Write(c, tmp); } } p = ZeroMalloc(sizeof(LOCAL_CONSOLE_PARAM)); c->Param = p; p->InFile = in_io; p->OutFile = out_io; p->Win32_OldConsoleWidth = old_size; if (in_io != NULL) { UINT size; void *buf; size = FileSize(in_io); buf = ZeroMalloc(size + 1); FileRead(in_io, buf, size); p->InBuf = NewBuf(); WriteBuf(p->InBuf, buf, size); Free(buf); p->InBuf->Current = 0; } return c; } // Release Console void ConsoleLocalFree(CONSOLE *c) { LOCAL_CONSOLE_PARAM *p; // Validate arguments if (c == NULL) { return; } p = (LOCAL_CONSOLE_PARAM *)c->Param; #ifdef OS_WIN32 if (p->Win32_OldConsoleWidth != 0) { MsSetConsoleWidth(p->Win32_OldConsoleWidth); } #endif // OS_WIN32 if (p != NULL) { if (p->InFile != NULL) { FileClose(p->InFile); FreeBuf(p->InBuf); } if (p->OutFile != NULL) { FileClose(p->OutFile); } Free(p); } DeleteLock(c->OutputLock); // Memory release Free(c); } // Get the width of the screen UINT ConsoleLocalGetWidth(CONSOLE *c) { UINT ret = 0; // Validate arguments if (c == NULL) { return 0; } #ifdef OS_WIN32 ret = MsGetConsoleWidth(); #else // OS_WIN32 { struct winsize t; Zero(&t, sizeof(t)); if (ioctl(1, TIOCGWINSZ, &t) == 0) { ret = t.ws_col; } } #endif // OS_WIN32 return ret; } // Read one line from the console wchar_t *ConsoleLocalReadLine(CONSOLE *c, wchar_t *prompt, bool nofile) { wchar_t *ret; LOCAL_CONSOLE_PARAM *p; // Validate arguments if (c == NULL) { return NULL; } p = (LOCAL_CONSOLE_PARAM *)c->Param; if (prompt == NULL) { prompt = L">"; } ConsoleWriteOutFile(c, prompt, false); if (nofile == false && p->InBuf != NULL) { // Read the next line from the file ret = ConsoleReadNextFromInFile(c); if (ret != NULL) { // Display the pseudo prompt UniPrint(L"%s", prompt); // Display on the screen UniPrint(L"%s\n", ret); } } else { // Read the following line from the console ret = Prompt(prompt); } if (ret != NULL) { ConsoleWriteOutFile(c, ret, true); } else { ConsoleWriteOutFile(c, _UU("CON_USER_CANCEL"), true); } return ret; } // Read the password from the console char *ConsoleLocalReadPassword(CONSOLE *c, wchar_t *prompt) { char tmp[64]; // Validate arguments if (c == NULL) { return NULL; } if (prompt == NULL) { prompt = L"Password>"; } UniPrint(L"%s", prompt); ConsoleWriteOutFile(c, prompt, false); if (PasswordPrompt(tmp, sizeof(tmp))) { ConsoleWriteOutFile(c, L"********", true); return CopyStr(tmp); } else { ConsoleWriteOutFile(c, _UU("CON_USER_CANCEL"), true); return NULL; } } // Display a string to the console bool ConsoleLocalWrite(CONSOLE *c, wchar_t *str) { // Validate arguments if (c == NULL || str == NULL) { return false; } UniPrint(L"%s%s", str, (UniEndWith(str, L"\n") ? L"" : L"\n")); ConsoleWriteOutFile(c, str, true); return true; } // Read the next line from the input file wchar_t *ConsoleReadNextFromInFile(CONSOLE *c) { LOCAL_CONSOLE_PARAM *p; char *str; // Validate arguments if (c == NULL) { return NULL; } p = (LOCAL_CONSOLE_PARAM *)c->Param; if (p->InBuf == NULL) { return NULL; } while (true) { str = CfgReadNextLine(p->InBuf); if (str == NULL) { return NULL; } Trim(str); if (IsEmptyStr(str) == false) { UINT size; wchar_t *ret; size = CalcUtf8ToUni((BYTE *)str, StrLen(str)); ret = ZeroMalloc(size + 32); Utf8ToUni(ret, size, (BYTE *)str, StrLen(str)); Free(str); return ret; } Free(str); } } // Write when the output file is specified void ConsoleWriteOutFile(CONSOLE *c, wchar_t *str, bool add_last_crlf) { LOCAL_CONSOLE_PARAM *p; // Validate arguments if (c == NULL || str == NULL) { return; } p = (LOCAL_CONSOLE_PARAM *)c->Param; if (p != NULL && p->OutFile != NULL) { wchar_t *tmp = UniNormalizeCrlf(str); UINT utf8_size; UCHAR *utf8; utf8_size = CalcUniToUtf8(tmp); utf8 = ZeroMalloc(utf8_size + 1); UniToUtf8(utf8, utf8_size + 1, tmp); FileWrite(p->OutFile, utf8, utf8_size); if (UniEndWith(str, L"\n") == false && add_last_crlf) { char *crlf = "\r\n"; FileWrite(p->OutFile, "\r\n", StrLen(crlf)); } Free(utf8); Free(tmp); } }