Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ns_cmdline.c
00001 /* 00002 * Copyright (c) 2016 ARM Limited. All rights reserved. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * Licensed under the Apache License, Version 2.0 (the License); you may 00005 * not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an AS IS BASIS, WITHOUT 00012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #include <stdio.h> 00018 #include <stdarg.h> 00019 #include <stdlib.h> 00020 #include <string.h> 00021 #include <ctype.h> 00022 00023 #if defined(_WIN32) || defined(__unix__) || defined(__unix) || defined(unix) || defined(YOTTA_CFG) 00024 #include <stdlib.h> //malloc 00025 #ifndef MEM_ALLOC 00026 #define MEM_ALLOC malloc 00027 #endif 00028 #ifndef MEM_FREE 00029 #define MEM_FREE free 00030 #endif 00031 #else 00032 #include "nsdynmemLIB.h" 00033 #ifndef MEM_ALLOC 00034 #define MEM_ALLOC ns_dyn_mem_temporary_alloc 00035 #endif 00036 #ifndef MEM_FREE 00037 #define MEM_FREE ns_dyn_mem_free 00038 #endif 00039 #endif 00040 00041 // force traces for this module 00042 //#define FEA_TRACE_SUPPORT 00043 00044 00045 #ifdef YOTTA_CFG 00046 #include "ns_list_internal/ns_list.h" 00047 #include "mbed-client-cli/ns_cmdline.h" 00048 #else 00049 #include "ns_list.h" 00050 #include "ns_cmdline.h" 00051 #endif 00052 #include "mbed-trace/mbed_trace.h" 00053 00054 //#define TRACE_DEEP 00055 //#define TRACE_PRINTF 00056 00057 #ifdef TRACE_PRINTF 00058 #undef tr_debug 00059 #define tr_debug(...) printf( __VA_ARGS__);printf("\r\n") 00060 #endif 00061 00062 // MBED_CLIENT_CLI_TRACE_ENABLE is to enable the traces for debugging, 00063 // default all debug traces are disabled. 00064 #ifndef MBED_CLIENT_CLI_TRACE_ENABLE 00065 #undef tr_error 00066 #define tr_error(...) 00067 #undef tr_warn 00068 #define tr_warn(...) 00069 #undef tr_debug 00070 #define tr_debug(...) 00071 #undef tr_info 00072 #define tr_info(...) 00073 #endif 00074 00075 #ifdef TRACE_DEEP 00076 #define tr_deep tr_debug 00077 #else 00078 #define tr_deep(...) 00079 #endif 00080 00081 #define TRACE_GROUP "cmdL" 00082 00083 /*ASCII defines*/ 00084 #define ESC 0x1B 00085 #define DEL 0x7F 00086 #define BS 0x08 00087 #define ETX 0x03 00088 #define TAB 0x09 00089 #define CAN 0x18 00090 00091 // Maximum length of input line 00092 #ifdef MBED_CMDLINE_MAX_LINE_LENGTH 00093 #define MAX_LINE MBED_CMDLINE_MAX_LINE_LENGTH 00094 #else 00095 #define MAX_LINE 2000 00096 #endif 00097 // Maximum number of arguments in a single command 00098 #ifdef MBED_CMDLINE_ARGUMENTS_MAX_COUNT 00099 #define MAX_ARGUMENTS MBED_CMDLINE_ARGUMENTS_MAX_COUNT 00100 #else 00101 #define MAX_ARGUMENTS 30 00102 #endif 00103 // Maximum number of commands saved in history 00104 #ifdef MBED_CMDLINE_HISTORY_MAX_COUNT 00105 #define HISTORY_MAX_COUNT MBED_CMDLINE_HISTORY_MAX_COUNT 00106 #else 00107 #define HISTORY_MAX_COUNT 32 00108 #endif 00109 00110 //include manuals or not (save memory a little when not include) 00111 #define INCLUDE_MAN 00112 00113 00114 typedef struct cmd_history_s { 00115 char *command_ptr; 00116 ns_list_link_t link; 00117 } cmd_history_t; 00118 00119 typedef struct cmd_command_s { 00120 const char *name_ptr; 00121 const char *info_ptr; 00122 const char *man_ptr; 00123 cmd_run_cb *run_cb; 00124 bool busy; 00125 ns_list_link_t link; 00126 } cmd_command_t; 00127 00128 typedef struct cmd_alias_s { 00129 char *name_ptr; 00130 char *value_ptr; 00131 ns_list_link_t link; 00132 } cmd_alias_t; 00133 00134 typedef struct cmd_variable_s { 00135 char *name_ptr; 00136 char *value_ptr; 00137 ns_list_link_t link; 00138 } cmd_variable_t; 00139 00140 typedef enum operator_s { 00141 OPERATOR_SEMI_COLON, //default 00142 OPERATOR_AND, 00143 OPERATOR_OR, 00144 OPERATOR_BACKGROUND, 00145 OPERATOR_PIPE 00146 } operator_t; 00147 typedef struct cmd_exe_s { 00148 char *cmd_s; 00149 operator_t operator; 00150 ns_list_link_t link; 00151 } cmd_exe_t; 00152 typedef NS_LIST_HEAD (cmd_exe_t, link) cmd_list_t; 00153 typedef NS_LIST_HEAD (cmd_history_t, link) history_list_t; 00154 typedef NS_LIST_HEAD (cmd_command_t, link) command_list_t; 00155 typedef NS_LIST_HEAD (cmd_alias_t, link) alias_list_t; 00156 typedef NS_LIST_HEAD (cmd_variable_t, link) variable_list_t; 00157 00158 typedef struct cmd_class_s { 00159 char input[MAX_LINE]; // input data 00160 char escape[10]; // escape data 00161 int16_t history; // history position 00162 int16_t cursor; // cursor position 00163 int16_t escape_index; // escape index 00164 history_list_t history_list; // input history 00165 uint8_t history_max_count; // history max size 00166 command_list_t command_list; // commands list 00167 alias_list_t alias_list; // alias list 00168 variable_list_t variable_list; // variables list 00169 bool vt100_on; // control characters 00170 bool init; // true when lists are initialized already 00171 bool escaping; // escaping input 00172 bool insert; // insert enabled 00173 int tab_lookup; // originally lookup characters count 00174 int tab_lookup_cmd_n; // index in command list 00175 int tab_lookup_n; // 00176 bool echo; // echo inputs 00177 char *retcode_fmt; // retcode format 00178 bool print_retcode; // print retcode after command is executed 00179 cmd_ready_cb_f *ready_cb; // ready cb function 00180 cmd_list_t cmd_buffer; 00181 cmd_exe_t *cmd_buffer_ptr; 00182 cmd_command_t *cmd_ptr; 00183 int8_t tasklet_id; 00184 int8_t network_tasklet_id; 00185 bool idle; 00186 00187 cmd_print_t *out; // print cb function 00188 void (*ctrl_fnc)(uint8_t c); // control cb function 00189 void (*mutex_wait_fnc)(void); // mutex wait cb function 00190 void (*mutex_release_fnc)(void); // mutex release cb function 00191 input_passthrough_func_t passthrough_fnc; // input passthrough cb function 00192 } cmd_class_t; 00193 00194 cmd_class_t cmd = { .init = false, .retcode_fmt = NULL, .cmd_ptr = NULL, .mutex_wait_fnc = NULL, .mutex_release_fnc = NULL, .passthrough_fnc = NULL }; 00195 00196 /* Function prototypes 00197 */ 00198 static void cmd_init_base_commands(void); 00199 static void cmd_replace_alias(char *input); 00200 static void cmd_replace_variables(char *input); 00201 static int cmd_parse_argv(char *string_ptr, char **argv); 00202 static void cmd_execute(void); 00203 static void cmd_line_clear(int from); 00204 static void cmd_history_save(int16_t index); 00205 static void cmd_history_get(uint16_t index); 00206 static void cmd_history_clean_overflow(void); 00207 static void cmd_history_clean(void); 00208 static void cmd_echo(bool on); 00209 static cmd_history_t *cmd_history_find(int16_t index); 00210 static bool cmd_tab_lookup(void); 00211 static const char *cmd_input_lookup(char *name, int namelength, int n); 00212 static char *cmd_input_lookup_var(char *name, int namelength, int n); 00213 static cmd_command_t *cmd_find(const char *name); 00214 static cmd_command_t *cmd_find_n(char *name, int nameLength, int n); 00215 static cmd_alias_t *alias_find(const char *alias); 00216 static cmd_alias_t *alias_find_n(char *alias, int aliaslength, int n); 00217 static cmd_variable_t *variable_find(char *variable); 00218 static cmd_variable_t *variable_find_n(char *variable, int length, int n); 00219 static void cmd_print_man(cmd_command_t *command_ptr); 00220 static void goto_end_of_history(void); 00221 static void goto_beginning_of_history(void); 00222 static void cmd_set_input(const char *str, int cur); 00223 static void cmd_set_retfmt(char *fmt); 00224 static char *next_command(char *string_ptr, operator_t *mode); 00225 /** Run single command through cmd intepreter 00226 * \param string_ptr command string with parameters 00227 * \ret command return code (CMDLINE_RETCODE_*) 00228 */ 00229 static int cmd_run(char *string_ptr); 00230 static cmd_exe_t *cmd_next_ptr(int retcode); 00231 static void cmd_split(char *string_ptr); 00232 static void cmd_push(char *cmd_str, operator_t oper); 00233 00234 /*internal shell commands 00235 */ 00236 int echo_command(int argc, char *argv[]); 00237 int alias_command(int argc, char *argv[]); 00238 int set_command(int argc, char *argv[]); 00239 int clear_command(int argc, char *argv[]); 00240 int help_command(int argc, char *argv[]); 00241 int history_command(int argc, char *argv[]); 00242 00243 void default_cmd_response_out(const char *fmt, va_list ap) 00244 { 00245 vprintf(fmt, ap); 00246 fflush(stdout); 00247 } 00248 void cmd_printf(const char *fmt, ...) 00249 { 00250 va_list ap; 00251 va_start(ap, fmt); 00252 cmd_vprintf(fmt, ap); 00253 va_end(ap); 00254 } 00255 void cmd_vprintf(const char *fmt, va_list ap) 00256 { 00257 if (cmd.mutex_wait_fnc) { 00258 cmd.mutex_wait_fnc(); 00259 } 00260 cmd.out(fmt, ap); 00261 if (cmd.mutex_release_fnc) { 00262 cmd.mutex_release_fnc(); 00263 } 00264 } 00265 /* Function definitions 00266 */ 00267 void cmd_init(cmd_print_t *outf) 00268 { 00269 if (!cmd.init) { 00270 ns_list_init(&cmd.alias_list); 00271 ns_list_init(&cmd.history_list); 00272 ns_list_init(&cmd.command_list); 00273 ns_list_init(&cmd.variable_list); 00274 ns_list_init(&cmd.cmd_buffer); 00275 cmd.init = true; 00276 } 00277 cmd.out = outf ? outf : default_cmd_response_out; 00278 cmd.ctrl_fnc = NULL; 00279 cmd.echo = true; 00280 cmd.print_retcode = false; 00281 cmd.escaping = false; 00282 cmd.insert = true; 00283 cmd.cursor = 0; 00284 cmd.vt100_on = true; 00285 cmd.history_max_count = HISTORY_MAX_COUNT; 00286 cmd.tab_lookup = 0; 00287 cmd.tab_lookup_cmd_n = 0; 00288 cmd.tab_lookup_n = 0; 00289 cmd.cmd_buffer_ptr = 0; 00290 cmd.idle = true; 00291 cmd.ready_cb = cmd_next; 00292 cmd.passthrough_fnc = NULL; 00293 cmd_set_retfmt("retcode: %i\r\n"); 00294 cmd_line_clear(0); // clear line 00295 cmd_history_save(0); // the current line is the 0 item 00296 //cmd_free(); 00297 cmd_init_base_commands(); 00298 cmd_init_screen(); 00299 return; 00300 } 00301 00302 #ifdef INCLUDE_MAN 00303 #define MAN_ECHO "Displays messages, or turns command echoing on or off\r\n"\ 00304 "echo <data_to_be_print>\r\n"\ 00305 "some special parameters:\r\n"\ 00306 "<bool> On/Off echo input characters\r\n" 00307 #define MAN_ALIAS "alias <theAlias> <command (parameters)>\r\n" 00308 #define MAN_SET "set <var_name> <value>\r\n"\ 00309 "some special parameters\r\n"\ 00310 "--vt100 <bool> On/Off vt100 controls\r\n"\ 00311 "--retcode <bool> On/Off retcode print after execution\r\n"\ 00312 "--retfmt <format> Return print format. Default: \"retcode: %i\\n\"\r\n" 00313 00314 #define MAN_CLEAR "Clears the display" 00315 #define MAN_HISTORY "Show commands history\r\n"\ 00316 "history (<optio>)\r\n"\ 00317 "clear Clear history\r\n" 00318 #else 00319 #define MAN_ECHO NULL 00320 #define MAN_ALIAS NULL 00321 #define MAN_SET NULL 00322 #define MAN_CLEAR NULL 00323 #define MAN_HISTORY NULL 00324 #endif 00325 00326 static void cmd_init_base_commands(void) 00327 { 00328 cmd_add("help", help_command, "This help", NULL); 00329 cmd_add("echo", echo_command, "Echo controlling", MAN_ECHO); 00330 cmd_add("alias", alias_command, "Handle aliases", MAN_ALIAS); 00331 cmd_add("set", set_command, "Handle variables", MAN_SET); 00332 cmd_add("clear", clear_command, "Clears the display", MAN_CLEAR); 00333 cmd_add("history", history_command, "View your command Line History", MAN_HISTORY); 00334 } 00335 void cmd_reset(void) 00336 { 00337 cmd_free(); 00338 cmd_init_base_commands(); 00339 } 00340 void cmd_free(void) 00341 { 00342 ns_list_foreach_safe(cmd_command_t, cur_ptr, &cmd.command_list) { 00343 cmd_delete(cur_ptr->name_ptr); 00344 } 00345 ns_list_foreach_safe(cmd_alias_t, cur_ptr, &cmd.alias_list) { 00346 cmd_alias_add(cur_ptr->name_ptr, NULL); 00347 } 00348 ns_list_foreach_safe(cmd_variable_t, cur_ptr, &cmd.variable_list) { 00349 cmd_variable_add(cur_ptr->name_ptr, NULL); 00350 } 00351 ns_list_foreach_safe(cmd_history_t, cur_ptr, &cmd.history_list) { 00352 MEM_FREE(cur_ptr->command_ptr); 00353 ns_list_remove(&cmd.history_list, cur_ptr); 00354 MEM_FREE(cur_ptr); 00355 } 00356 cmd.mutex_wait_fnc = NULL; 00357 cmd.mutex_release_fnc = NULL; 00358 } 00359 00360 void cmd_input_passthrough_func(input_passthrough_func_t passthrough_fnc) 00361 { 00362 cmd.passthrough_fnc = passthrough_fnc; 00363 } 00364 00365 void cmd_exe(char *str) 00366 { 00367 cmd_split(str); 00368 if (cmd.cmd_buffer_ptr == 0) { 00369 //execution buffer is empty 00370 cmd.idle = false; //not really, but fake it 00371 cmd_ready(CMDLINE_RETCODE_SUCCESS); 00372 } else { 00373 tr_debug("previous cmd is still in progress"); 00374 } 00375 } 00376 void cmd_set_ready_cb(cmd_ready_cb_f *cb) 00377 { 00378 cmd.ready_cb = cb; 00379 } 00380 void cmd_ready(int retcode) 00381 { 00382 if( cmd.cmd_ptr && cmd.cmd_ptr->busy ) 00383 { 00384 //execution finished 00385 cmd.cmd_ptr->busy = false; 00386 } 00387 if (!cmd.idle) { 00388 if (cmd.cmd_buffer_ptr == NULL) { 00389 tr_debug("goto next command"); 00390 } else { 00391 tr_debug("cmd '%s' executed with retcode: %i", cmd.cmd_buffer_ptr->cmd_s, retcode); 00392 } 00393 if (cmd.ready_cb == NULL) { 00394 tr_warn("Missing ready_cb! use cmd_set_ready_cb()"); 00395 } else { 00396 cmd.ready_cb(retcode); 00397 } 00398 } else { 00399 tr_warn("Someone call cmd_ready(%i) even there shouldn't be any running cmd", retcode); 00400 if (cmd.echo) { 00401 cmd_output(); //refresh if this happens 00402 } 00403 } 00404 } 00405 void cmd_next(int retcode) 00406 { 00407 cmd.idle = true; 00408 //figure out next command 00409 cmd.cmd_buffer_ptr = cmd_next_ptr(retcode); 00410 if (cmd.cmd_buffer_ptr) { 00411 cmd.idle = false; 00412 //yep there was some -> lets execute it 00413 retcode = cmd_run(cmd.cmd_buffer_ptr->cmd_s); 00414 //check if execution goes to the backend or not 00415 if (retcode == CMDLINE_RETCODE_EXCUTING_CONTINUE ) { 00416 if( (NULL != cmd.cmd_buffer_ptr) && cmd.cmd_buffer_ptr->operator == OPERATOR_BACKGROUND ) 00417 { 00418 //execution continue in background, but operator say that it's "ready" 00419 cmd_ready(CMDLINE_RETCODE_SUCCESS); 00420 } else { 00421 //command execution phase continuous in background 00422 tr_debug("Command execution continuous in background.."); 00423 } 00424 } else { 00425 //execution finished -> call ready function with retcode 00426 cmd_ready(retcode); 00427 } 00428 } else { 00429 if (cmd.print_retcode) { 00430 cmd_printf(cmd.retcode_fmt, retcode); 00431 } 00432 cmd_line_clear(0); 00433 if (cmd.echo) { 00434 cmd_output(); //ready 00435 } 00436 } 00437 } 00438 static cmd_exe_t *cmd_pop(void) 00439 { 00440 cmd_exe_t *cmd_ptr = ns_list_get_first(&cmd.cmd_buffer), 00441 *next_cmd = ns_list_get_next(&cmd.cmd_buffer, cmd_ptr); 00442 00443 if (cmd.cmd_buffer_ptr == 0) { 00444 //was first in bool-- 00445 next_cmd = ns_list_get_first(&cmd.cmd_buffer); 00446 } else { 00447 MEM_FREE(cmd_ptr->cmd_s); 00448 ns_list_remove(&cmd.cmd_buffer, cmd_ptr); 00449 MEM_FREE(cmd_ptr); 00450 } 00451 return next_cmd; 00452 } 00453 00454 static cmd_exe_t *cmd_next_ptr(int retcode) 00455 { 00456 cmd_exe_t *next_cmd = cmd.cmd_buffer_ptr; 00457 if (ns_list_is_empty(&cmd.cmd_buffer)) { 00458 return NULL; 00459 } 00460 if (cmd.cmd_buffer_ptr == NULL) { 00461 return cmd_pop(); 00462 } 00463 retcode = retcode; 00464 switch (cmd.cmd_buffer_ptr->operator) { 00465 case (OPERATOR_AND): 00466 if (retcode != CMDLINE_RETCODE_SUCCESS) { 00467 //if fails, go to next command, which not have AND operator 00468 while ((next_cmd->operator == OPERATOR_AND) && ((next_cmd = cmd_pop()) != 0)); 00469 } else { 00470 next_cmd = cmd_pop(); 00471 } 00472 break; 00473 case (OPERATOR_OR): 00474 if (retcode == CMDLINE_RETCODE_SUCCESS) { 00475 //if fails, go to next command, which not have OR operator 00476 while ((next_cmd->operator == OPERATOR_OR) && ((next_cmd = cmd_pop()) != 0)); 00477 } else { 00478 next_cmd = cmd_pop(); 00479 } 00480 break; 00481 case (OPERATOR_BACKGROUND): 00482 next_cmd = cmd_pop(); 00483 break; 00484 case (OPERATOR_PIPE): 00485 /// @TODO 00486 case (OPERATOR_SEMI_COLON): 00487 default: 00488 //get next command to be execute (might be null if there is no more) 00489 next_cmd = cmd_pop(); 00490 break; 00491 } 00492 00493 //return next command if any 00494 return next_cmd; 00495 } 00496 static void cmd_split(char *string_ptr) 00497 { 00498 char *ptr = string_ptr, *next; 00499 operator_t oper = OPERATOR_SEMI_COLON; 00500 do { 00501 next = next_command(ptr, &oper); 00502 cmd_push(ptr, oper); 00503 ptr = next; 00504 if (next && !*next) { 00505 break; 00506 } 00507 } while (ptr != 0); 00508 } 00509 00510 static void cmd_push(char *cmd_str, operator_t oper) 00511 { 00512 //store this command to the stack 00513 cmd_exe_t *cmd_ptr = MEM_ALLOC(sizeof(cmd_exe_t)); 00514 cmd_ptr->cmd_s = MEM_ALLOC(strlen(cmd_str) + 1); 00515 strcpy(cmd_ptr->cmd_s, cmd_str); 00516 cmd_ptr->operator = oper; 00517 ns_list_add_to_end(&cmd.cmd_buffer, cmd_ptr); 00518 } 00519 void cmd_out_func(cmd_print_t *outf) 00520 { 00521 cmd.out = outf; 00522 } 00523 void cmd_ctrl_func(void (*sohf)(uint8_t c)) 00524 { 00525 cmd.ctrl_fnc = sohf; 00526 } 00527 00528 void cmd_mutex_wait_func(void (*mutex_wait_f)(void)) 00529 { 00530 cmd.mutex_wait_fnc = mutex_wait_f; 00531 } 00532 void cmd_mutex_release_func(void (*mutex_release_f)(void)) 00533 { 00534 cmd.mutex_release_fnc = mutex_release_f; 00535 } 00536 00537 void cmd_mutex_lock() 00538 { 00539 if (cmd.mutex_wait_fnc) { 00540 cmd.mutex_wait_fnc(); 00541 } 00542 } 00543 00544 void cmd_mutex_unlock() 00545 { 00546 if (cmd.mutex_release_fnc) { 00547 cmd.mutex_release_fnc(); 00548 } 00549 } 00550 00551 void cmd_init_screen() 00552 { 00553 if (cmd.vt100_on) { 00554 cmd_printf("\r\x1b[2J"); /* Clear screen */ 00555 cmd_printf("\x1b[7h"); /* enable line wrap */ 00556 } 00557 cmd_printf("ARM Ltd\r\n"); 00558 cmd_output(); 00559 } 00560 uint8_t cmd_history_size(uint8_t max) 00561 { 00562 if (max > 0) { 00563 cmd.history_max_count = max; 00564 cmd_history_clean_overflow(); 00565 } 00566 return cmd.history_max_count; 00567 } 00568 static void cmd_echo(bool on) 00569 { 00570 cmd.echo = on; 00571 } 00572 bool cmd_echo_state(void) 00573 { 00574 return cmd.echo; 00575 } 00576 static cmd_command_t *cmd_find_n(char *name, int nameLength, int n) 00577 { 00578 cmd_command_t *cmd_ptr = NULL; 00579 int i = 0; 00580 if (name != NULL && nameLength != 0) { 00581 ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) { 00582 if (strncmp(name, cur_ptr->name_ptr, nameLength) == 0) { 00583 if (i == n) { 00584 cmd_ptr = cur_ptr; 00585 break; 00586 } 00587 i++; 00588 } 00589 } 00590 } 00591 return cmd_ptr; 00592 } 00593 static const char *cmd_input_lookup(char *name, int namelength, int n) 00594 { 00595 const char *str = NULL; 00596 cmd_command_t *cmd_ptr = cmd_find_n(name, namelength, n); 00597 if (cmd_ptr) { 00598 str = cmd_ptr->name_ptr; 00599 cmd.tab_lookup_n = n + 1; 00600 } else { 00601 n -= cmd.tab_lookup_n; 00602 cmd_alias_t *alias = alias_find_n(name, namelength, n); 00603 if (alias) { 00604 str = (const char*)alias->name_ptr; 00605 } 00606 } 00607 00608 return str; 00609 } 00610 static char *cmd_input_lookup_var(char *name, int namelength, int n) 00611 { 00612 char *str = NULL; 00613 cmd_variable_t *var = variable_find_n(name, namelength, n); 00614 if (var) { 00615 str = var->name_ptr; 00616 } 00617 return str; 00618 } 00619 static cmd_command_t *cmd_find(const char *name) 00620 { 00621 cmd_command_t *cmd_ptr = NULL; 00622 if (name == NULL || strlen(name) == 0) { 00623 tr_error("cmd_find invalid parameters"); 00624 return NULL; 00625 } 00626 00627 ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) { 00628 if (strcmp(name, cur_ptr->name_ptr) == 0) { 00629 cmd_ptr = cur_ptr; 00630 break; 00631 } 00632 } 00633 return cmd_ptr; 00634 } 00635 00636 void cmd_add(const char *name, cmd_run_cb *callback, const char *info, const char *man) 00637 { 00638 cmd_command_t *cmd_ptr; 00639 00640 if (name == NULL || callback == NULL || strlen(name) == 0) { 00641 tr_warn("cmd_add invalid parameters"); 00642 return; 00643 } 00644 cmd_ptr = (cmd_command_t *)MEM_ALLOC(sizeof(cmd_command_t)); 00645 cmd_ptr->name_ptr = name; 00646 cmd_ptr->info_ptr = info; 00647 cmd_ptr->man_ptr = man; 00648 cmd_ptr->run_cb = callback; 00649 cmd_ptr->busy = false; 00650 ns_list_add_to_end(&cmd.command_list, cmd_ptr); 00651 return; 00652 } 00653 00654 void cmd_delete(const char *name) 00655 { 00656 cmd_command_t *cmd_ptr; 00657 cmd_ptr = cmd_find(name); 00658 if (cmd_ptr == NULL) { 00659 return; 00660 } 00661 ns_list_remove(&cmd.command_list, cmd_ptr); 00662 MEM_FREE(cmd_ptr); 00663 return; 00664 } 00665 00666 static int cmd_parse_argv(char *string_ptr, char **argv) 00667 { 00668 int argc = 0; 00669 char *str_ptr, *end_quote_ptr = NULL; 00670 00671 if (string_ptr == NULL || strlen(string_ptr) == 0) { 00672 tr_error("Invalid parameters"); 00673 return 0; 00674 } 00675 str_ptr = string_ptr; 00676 do { 00677 argv[argc] = str_ptr; 00678 if (*str_ptr == '"') { 00679 // check if end quote 00680 end_quote_ptr = strstr(str_ptr + 1, "\""); 00681 } 00682 if (*str_ptr == '"' && end_quote_ptr != NULL) { 00683 // remove quotes give as one parameter 00684 argv[argc]++; 00685 str_ptr = end_quote_ptr; 00686 } else { 00687 str_ptr = strstr(str_ptr, " "); 00688 } 00689 argc++; 00690 if (str_ptr == NULL) { 00691 break; 00692 } 00693 if (argc > MAX_ARGUMENTS) { 00694 break; 00695 } 00696 *str_ptr++ = 0; 00697 while (*str_ptr == ' ') { 00698 str_ptr++; // skip spaces 00699 } 00700 } while (*str_ptr != 0); 00701 return argc; 00702 } 00703 static void cmd_print_man(cmd_command_t *command_ptr) 00704 { 00705 if (command_ptr->man_ptr) { 00706 cmd_printf("%s\r\n", command_ptr->man_ptr); 00707 } 00708 } 00709 static void cmd_set_input(const char *str, int cur) 00710 { 00711 cmd_line_clear(cur); 00712 strcpy(cmd.input + cur, str); 00713 cmd.cursor = strlen(cmd.input); 00714 } 00715 /** 00716 * If oper is not null, function set null pointers 00717 */ 00718 static char *next_command(char *string_ptr, operator_t *oper) 00719 { 00720 char *ptr = string_ptr; 00721 bool quote = false; 00722 while (*ptr != 0) { 00723 if (quote) { 00724 if (*ptr == '"') { 00725 quote = false; 00726 } 00727 } else { 00728 //skip if previous character is '\' 00729 if ((ptr == string_ptr) || (*(ptr - 1) != '\\')) { 00730 switch (*ptr) { 00731 case ('"'): { 00732 quote = true; 00733 break; 00734 } 00735 case (';'): //default operator 00736 if (oper) { 00737 *oper = OPERATOR_SEMI_COLON; 00738 *ptr = 0; 00739 } 00740 return ptr + 1; 00741 case ('&'): 00742 if (ptr[1] == '&') { 00743 if (oper) { 00744 *oper = OPERATOR_AND; 00745 *ptr = 0; 00746 } 00747 return ptr + 2; 00748 } else { 00749 if (oper) { 00750 *oper = OPERATOR_BACKGROUND; 00751 *ptr = 0; 00752 } 00753 return ptr + 1; 00754 } 00755 case ('|'): 00756 if (ptr[1] == '|') { 00757 tr_warn("or operator not supported"); 00758 if (oper) { 00759 *oper = OPERATOR_OR; 00760 *ptr = 0; 00761 } 00762 return ptr + 2; 00763 } else { 00764 tr_warn("pipe operator not supported"); 00765 if (oper) { 00766 *oper = OPERATOR_PIPE; 00767 *ptr = 0; 00768 } 00769 return ptr + 1; 00770 } 00771 default: 00772 break; 00773 } 00774 } 00775 } 00776 ptr++; 00777 } 00778 return 0; 00779 } 00780 static int cmd_run(char *string_ptr) 00781 { 00782 char *argv[MAX_ARGUMENTS]; 00783 int argc, ret; 00784 00785 tr_info("Executing cmd: '%s'", string_ptr); 00786 char *command_str = MEM_ALLOC(MAX_LINE); 00787 while (isspace((unsigned char) *string_ptr) && 00788 *string_ptr != '\n' && 00789 *string_ptr != 0) { 00790 string_ptr++; //skip white spaces 00791 } 00792 strcpy(command_str, string_ptr); 00793 tr_deep("cmd_run('%s') ", command_str); 00794 cmd_replace_alias(command_str); 00795 cmd_replace_variables(command_str); 00796 tr_debug("Parsed cmd: '%s'", command_str); 00797 00798 argc = cmd_parse_argv(command_str, argv); 00799 00800 cmd.cmd_ptr = cmd_find(argv[0]); 00801 00802 if (cmd.cmd_ptr == NULL) { 00803 cmd_printf("Command '%s' not found.\r\n", argv[0]); 00804 MEM_FREE(command_str); 00805 return CMDLINE_RETCODE_COMMAND_NOT_FOUND; 00806 } 00807 if (cmd.cmd_ptr->run_cb == NULL) { 00808 tr_error("Command callback missing"); 00809 MEM_FREE(command_str); 00810 return CMDLINE_RETCODE_COMMAND_CB_MISSING; 00811 } 00812 00813 if (argc == 2 && 00814 (cmd_has_option(argc, argv, "h") || cmd_parameter_index(argc, argv, "--help") > 0)) { 00815 MEM_FREE(command_str); 00816 cmd_print_man(cmd.cmd_ptr); 00817 return CMDLINE_RETCODE_SUCCESS; 00818 } 00819 00820 if( cmd.cmd_ptr->busy ) { 00821 MEM_FREE(command_str); 00822 return CMDLINE_RETCODE_COMMAND_BUSY; 00823 } 00824 00825 // Run the actual callback 00826 cmd.cmd_ptr->busy = true; 00827 ret = cmd.cmd_ptr->run_cb(argc, argv); 00828 #ifndef TEST 00829 cmd_alias_add("_", command_str); // last executed command 00830 #endif 00831 MEM_FREE(command_str); 00832 switch (ret) { 00833 case (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED): 00834 tr_warn("Command not implemented"); 00835 break; 00836 case (CMDLINE_RETCODE_INVALID_PARAMETERS): 00837 tr_warn("Command parameter was incorrect"); 00838 cmd_printf("Invalid parameters!\r\n"); 00839 cmd_print_man(cmd.cmd_ptr); 00840 break; 00841 default: 00842 break; 00843 } 00844 return ret; 00845 } 00846 00847 void cmd_escape_start(void) 00848 { 00849 cmd.escaping = true; 00850 memset(cmd.escape, 0, sizeof(cmd.escape)); 00851 cmd.escape_index = 0; 00852 } 00853 char *strrevchr(const char *begin, const char *end, char ch) 00854 { 00855 char *ptr = (char *)end; 00856 while (begin <= ptr) { 00857 if (*ptr == ch) { 00858 return ptr; 00859 } 00860 if (*ptr == 0) { 00861 return ptr; 00862 } 00863 ptr--; 00864 } 00865 return 0; 00866 } 00867 void cmd_escape_read(int16_t u_data) 00868 { 00869 int16_t old_entry; 00870 00871 tr_debug("cmd_escape_read: %02x '%c'", u_data, (isprint(u_data) ? u_data : '?')); 00872 00873 if (u_data == '[') { 00874 /*first character for longer escape sequence ends in character*/ 00875 cmd.escape[cmd.escape_index++] = '['; 00876 return; 00877 } 00878 if (u_data == 'D') { 00879 /* Arrow left*/ 00880 if ((cmd.escape_index == 1 && cmd.escape[0] == 'O') || 00881 (cmd.escape_index == 4 && strncmp(cmd.escape + 1, "1;5", 3) == 0)) { 00882 00883 char *ptr = cmd.input + cmd.cursor; 00884 if (*ptr == ' ' || *ptr == 0) { 00885 ptr--; 00886 } 00887 ptr = strrevchr(cmd.input, ptr, ' '); //@todo not working way that we want 00888 if (ptr) { 00889 cmd.cursor = ptr - cmd.input; 00890 } else { 00891 cmd.cursor = 0; 00892 } 00893 } else { 00894 cmd.cursor --; 00895 } 00896 if (cmd.cursor < 0) { 00897 cmd.cursor = 0; 00898 } 00899 } else if (u_data == 'C') { 00900 /* Arrow Right*/ 00901 if ((cmd.escape_index == 1 && cmd.escape[0] == 'O') || 00902 (cmd.escape_index == 4 && strncmp(cmd.escape + 1, "1;5", 3) == 0)) { 00903 char *ptr = cmd.input + cmd.cursor; 00904 if (*ptr == ' ') { 00905 ptr++; 00906 } 00907 ptr = strchr(ptr, ' '); 00908 if (ptr) { 00909 cmd.cursor = ptr - cmd.input; 00910 } else { 00911 cmd.cursor = strlen(cmd.input); 00912 } 00913 } else { 00914 cmd.cursor ++; 00915 } 00916 if ((int)cmd.cursor > (int)strlen(cmd.input)) { 00917 cmd.cursor = strlen(cmd.input); 00918 } 00919 } else if (u_data == 'A') { 00920 /* Arrow Up*/ 00921 old_entry = cmd.history++; 00922 if (NULL == cmd_history_find(cmd.history)) { 00923 cmd.history = old_entry; 00924 } 00925 if (old_entry != cmd.history) { 00926 cmd_history_save(old_entry); 00927 cmd_history_get(cmd.history); 00928 } 00929 } else if (u_data == 'B') { 00930 /* Arrow Down*/ 00931 old_entry = cmd.history--; 00932 if (cmd.history < 0) { 00933 cmd.history = 0; 00934 } 00935 00936 if (old_entry != cmd.history) { 00937 cmd_history_save(old_entry); 00938 cmd_history_get(cmd.history); 00939 } 00940 } else if (u_data == 'Z') { 00941 // Shift+TAB 00942 if (cmd.tab_lookup > 0) { 00943 cmd.cursor = cmd.tab_lookup; 00944 cmd.input[cmd.tab_lookup] = 0; 00945 if (cmd.tab_lookup_cmd_n > 0) { 00946 cmd.tab_lookup_cmd_n--; 00947 } 00948 } 00949 cmd_tab_lookup(); 00950 } else if (u_data == 'H') { 00951 // Xterm support 00952 cmd.cursor = 0; 00953 } else if (u_data == 'F') { 00954 // Xterm support 00955 cmd.cursor = strlen(cmd.input); 00956 } else if (isdigit((int)cmd.escape[cmd.escape_index - 1]) && u_data == '~') { 00957 switch (cmd.escape[cmd.escape_index - 1]) { 00958 case ('1'): //beginning-of-line # Home key 00959 cmd.cursor = 0; 00960 break; 00961 case ('2'): //quoted-insert # Insert key 00962 cmd.insert = !cmd.insert; 00963 break; 00964 case ('3'): //delete-char # Delete key 00965 if ((int)strlen(cmd.input) > (int)cmd.cursor) { 00966 memmove(&cmd.input[cmd.cursor], &cmd.input[cmd.cursor + 1], strlen(&cmd.input[cmd.cursor + 1]) + 1); 00967 } 00968 break; 00969 case ('4'): //end-of-line # End key 00970 cmd.cursor = strlen(cmd.input); 00971 break; 00972 case ('5'): //beginning-of-history # PageUp key 00973 goto_end_of_history(); 00974 break; 00975 case ('6'): //end-of-history # PageDown key 00976 goto_beginning_of_history(); 00977 break; 00978 default: 00979 break; 00980 } 00981 } else if (isprint(u_data)) { //IS_NUMBER || IS_CONTROL 00982 cmd.escape[cmd.escape_index++] = u_data; 00983 return; 00984 } 00985 cmd_output(); 00986 cmd.escaping = false; 00987 return; 00988 } 00989 static void goto_end_of_history(void) 00990 { 00991 // handle new input if any and verify that 00992 // it is not already in beginning of history or current position 00993 bool allowStore = strlen(cmd.input) != 0; //avoid store empty lines to history 00994 cmd_history_t *entry_ptr; 00995 if (cmd.history > 0 && allowStore) { 00996 entry_ptr = cmd_history_find(cmd.history); 00997 if (entry_ptr) { 00998 if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) { 00999 // current history contains contains same text as input 01000 allowStore = false; 01001 } 01002 } 01003 } else if (allowStore && (entry_ptr = cmd_history_find(0)) != NULL) { 01004 if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) { 01005 //beginning of history was same text as input 01006 allowStore = false; 01007 } 01008 } 01009 if (allowStore) { 01010 cmd_history_save(0); // new is saved to place 0 01011 cmd_history_save(-1); // new is created to the current one 01012 } 01013 01014 cmd_history_t *cmd_ptr = ns_list_get_last(&cmd.history_list); 01015 cmd_set_input(cmd_ptr->command_ptr, 0); 01016 cmd.history = ns_list_count(&cmd.history_list) - 1; 01017 } 01018 static void goto_beginning_of_history(void) 01019 { 01020 cmd_history_t *cmd_ptr = ns_list_get_first(&cmd.history_list); 01021 cmd_set_input(cmd_ptr->command_ptr, 0); 01022 cmd.history = 0; 01023 } 01024 static void cmd_reset_tab(void) 01025 { 01026 cmd.tab_lookup = 0; 01027 cmd.tab_lookup_cmd_n = 0; 01028 cmd.tab_lookup_n = 0; 01029 } 01030 void cmd_char_input(int16_t u_data) 01031 { 01032 /*Handle passthrough*/ 01033 if (cmd.passthrough_fnc != NULL) { 01034 cmd.passthrough_fnc(u_data); 01035 return; 01036 } 01037 01038 /*handle ecape command*/ 01039 if (cmd.escaping == true) { 01040 cmd_escape_read(u_data); 01041 return; 01042 } 01043 tr_debug("input char: %02x '%c', cursor: %i, input: \"%s\"", u_data, (isprint(u_data) ? u_data : ' '), cmd.cursor, cmd.input); 01044 01045 /*Normal character input*/ 01046 if (u_data == '\r' || u_data == '\n') { 01047 cmd_reset_tab(); 01048 if (strlen(cmd.input) == 0) { 01049 if (cmd.echo) { 01050 cmd_printf("\r\n"); 01051 cmd_output(); 01052 } 01053 } else { 01054 if (cmd.echo) { 01055 cmd_printf("\r\n"); 01056 } 01057 cmd_execute(); 01058 } 01059 } else if (u_data == ESC) { 01060 cmd_escape_start(); 01061 } else if (u_data == BS || u_data == DEL) { 01062 cmd_reset_tab(); 01063 cmd.cursor--; 01064 if (cmd.cursor < 0) { 01065 cmd.cursor = 0; 01066 return; 01067 } 01068 memmove(&cmd.input[cmd.cursor], &cmd.input[cmd.cursor + 1], strlen(&cmd.input[cmd.cursor + 1]) + 1); 01069 if (cmd.echo) { 01070 cmd_output(); 01071 } 01072 } else if (u_data == ETX || u_data == CAN) { 01073 //ctrl+c (End of text) or ctrl+x (cancel) 01074 cmd_reset_tab(); 01075 cmd_line_clear(0); 01076 if (!cmd.idle) { 01077 cmd_ready(CMDLINE_RETCODE_FAIL); 01078 } 01079 if (cmd.echo) { 01080 cmd_output(); 01081 } 01082 } else if (u_data == TAB) { 01083 bool inc = false; 01084 if (cmd.tab_lookup > 0) { 01085 cmd.cursor = cmd.tab_lookup; 01086 cmd.input[cmd.tab_lookup] = 0; 01087 cmd.tab_lookup_cmd_n++; 01088 inc = true; 01089 } else { 01090 cmd.tab_lookup = strlen(cmd.input); 01091 } 01092 01093 if (!cmd_tab_lookup()) { 01094 if (inc) { 01095 cmd.tab_lookup_cmd_n--; 01096 } 01097 memset(cmd.input + cmd.tab_lookup, 0, MAX_LINE - cmd.tab_lookup); 01098 } 01099 if (cmd.echo) { 01100 cmd_output(); 01101 } 01102 01103 } else if (iscntrl(u_data)) { 01104 if (cmd.ctrl_fnc) { 01105 cmd.ctrl_fnc(u_data); 01106 } 01107 } else { 01108 cmd_reset_tab(); 01109 tr_deep("cursor: %d, inputlen: %d, u_data: %c\r\n", cmd.cursor, strlen(cmd.input), u_data); 01110 if ((strlen(cmd.input) >= MAX_LINE - 1) || (cmd.cursor >= MAX_LINE - 1)) { 01111 tr_warn("input buffer full"); 01112 if (cmd.echo) { 01113 cmd_output(); 01114 } 01115 return; 01116 } 01117 if (cmd.insert) { 01118 memmove(&cmd.input[cmd.cursor + 1], &cmd.input[cmd.cursor], strlen(&cmd.input[cmd.cursor]) + 1); 01119 } 01120 cmd.input[cmd.cursor++] = u_data; 01121 if (cmd.echo) { 01122 cmd_output(); 01123 } 01124 } 01125 } 01126 static int check_variable_keylookup_size(char **key, int *keysize) 01127 { 01128 if (cmd.cursor > 0 && cmd.tab_lookup > 0) { 01129 //printf("tab_lookup: %i\r\n", cmd.tab_lookup); 01130 char *ptr = cmd.input + cmd.tab_lookup; 01131 do { 01132 //printf("varkey lookup: %c\r\n", *ptr); 01133 if (*ptr == ' ') { 01134 return 0; 01135 } 01136 if (*ptr == '$') { 01137 int varlen = cmd.tab_lookup - (ptr - cmd.input) - 1; 01138 *key = ptr; 01139 *keysize = varlen; 01140 //printf("varkey size: %i\r\n", varlen); 01141 return (ptr - cmd.input); 01142 } 01143 ptr--; 01144 } while (ptr > cmd.input); 01145 } 01146 return 0; 01147 } 01148 bool cmd_tab_lookup(void) 01149 { 01150 int len = strlen(cmd.input); 01151 if (len == 0) { 01152 return false; 01153 } 01154 01155 char *variable_keypart; 01156 int lookupSize; 01157 int varpos = check_variable_keylookup_size(&variable_keypart, &lookupSize); 01158 01159 const char *str = NULL; 01160 if (varpos) { 01161 str = cmd_input_lookup_var(variable_keypart + 1, lookupSize, cmd.tab_lookup_cmd_n); 01162 if (str) { 01163 cmd_set_input(str, varpos + 1); 01164 return true; 01165 } 01166 } else { 01167 str = cmd_input_lookup(cmd.input, len, cmd.tab_lookup_cmd_n); 01168 if (str != NULL) { 01169 cmd_set_input(str, 0); 01170 return true; 01171 } 01172 } 01173 01174 return false; 01175 } 01176 void cmd_output(void) 01177 { 01178 if (cmd.vt100_on && cmd.idle) { 01179 cmd_printf("\r\x1b[2K/>%s \x1b[%dD", cmd.input, (int)strlen(cmd.input) - cmd.cursor + 1); 01180 } 01181 } 01182 void cmd_echo_off(void) 01183 { 01184 cmd.echo = false; 01185 } 01186 void cmd_echo_on(void) 01187 { 01188 cmd.echo = true; 01189 } 01190 // alias 01191 #ifndef TEST 01192 static 01193 #endif 01194 int replace_alias(char *str, const char *old_str, const char *new_str) 01195 { 01196 int old_len = strlen(old_str), 01197 new_len = strlen(new_str); 01198 if ((strncmp(str, old_str, old_len) == 0) && 01199 ((str[ old_len ] == ' ') || (str[ old_len ] == 0) || 01200 (str[ old_len ] == ';') || (str[ old_len ] == '&'))) { 01201 memmove(str + new_len, str + old_len, strlen(str + old_len) + 1); 01202 memcpy(str, new_str, new_len); 01203 return new_len - old_len; 01204 } 01205 return 0; 01206 } 01207 static void cmd_replace_alias(char *input) 01208 { 01209 ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { 01210 replace_alias(input, cur_ptr->name_ptr, cur_ptr->value_ptr); 01211 } 01212 } 01213 //variable 01214 #ifndef TEST 01215 static 01216 #endif 01217 void replace_variable(char *str, const char *old_str, const char *new_str) 01218 { 01219 char *ptr = str; 01220 int old_len = strlen(old_str), 01221 new_len = strlen(new_str); 01222 if (old_len > 0) { 01223 while ((ptr = strstr(ptr, old_str)) != 0) { 01224 if (ptr > str && *(ptr - 1) == '$') { 01225 memmove(ptr + new_len - 1, ptr + old_len, strlen(ptr + old_len) + 1); 01226 memcpy(ptr - 1, new_str, new_len); 01227 } 01228 ptr++; 01229 } 01230 } 01231 } 01232 void cmd_replace_variables(char *input) 01233 { 01234 ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { 01235 replace_variable(input, cur_ptr->name_ptr, cur_ptr->value_ptr); 01236 } 01237 } 01238 //history 01239 static void cmd_history_item_delete(cmd_history_t *entry_ptr) 01240 { 01241 ns_list_remove(&cmd.history_list, entry_ptr); 01242 MEM_FREE(entry_ptr->command_ptr); 01243 MEM_FREE(entry_ptr); 01244 } 01245 01246 static cmd_history_t *cmd_history_find(int16_t index) 01247 { 01248 cmd_history_t *entry_ptr = NULL; 01249 int16_t count = 0; 01250 ns_list_foreach(cmd_history_t, cur_ptr, &cmd.history_list) { 01251 if (count == index) { 01252 entry_ptr = cur_ptr; 01253 break; 01254 } 01255 count++; 01256 } 01257 return entry_ptr; 01258 } 01259 01260 static void cmd_history_clean_overflow(void) 01261 { 01262 while (ns_list_count(&cmd.history_list) > cmd.history_max_count) { 01263 cmd_history_t *cmd_ptr = ns_list_get_last(&cmd.history_list); 01264 tr_debug("removing older history (%s)", cmd_ptr->command_ptr); 01265 cmd_history_item_delete(cmd_ptr); 01266 } 01267 } 01268 static void cmd_history_clean(void) 01269 { 01270 while (ns_list_count(&cmd.history_list) > 0) { 01271 tr_debug("removing older history"); 01272 cmd_history_item_delete(ns_list_get_last(&cmd.history_list)); 01273 } 01274 } 01275 static void cmd_history_save(int16_t index) 01276 { 01277 /*if entry true save it to first item which is the one currently edited*/ 01278 cmd_history_t *entry_ptr; 01279 int16_t len; 01280 01281 len = strlen(cmd.input); 01282 01283 tr_debug("saving history item %d", index); 01284 entry_ptr = cmd_history_find(index); 01285 01286 if (entry_ptr == NULL) { 01287 /*new entry*/ 01288 entry_ptr = (cmd_history_t *)MEM_ALLOC(sizeof(cmd_history_t)); 01289 entry_ptr->command_ptr = NULL; 01290 ns_list_add_to_start(&cmd.history_list, entry_ptr); 01291 } 01292 01293 if (entry_ptr->command_ptr != NULL) { 01294 MEM_FREE(entry_ptr->command_ptr); 01295 } 01296 entry_ptr->command_ptr = (char *)MEM_ALLOC(len + 1); 01297 strcpy(entry_ptr->command_ptr, cmd.input); 01298 01299 cmd_history_clean_overflow(); 01300 } 01301 static void cmd_history_get(uint16_t index) 01302 { 01303 cmd_history_t *entry_ptr; 01304 01305 tr_debug("getting history item %d", index); 01306 01307 entry_ptr = cmd_history_find(index); 01308 01309 if (entry_ptr != NULL) { 01310 memset(cmd.input, 0, MAX_LINE); 01311 cmd_set_input(entry_ptr->command_ptr, 0); 01312 } 01313 } 01314 01315 static void cmd_line_clear(int from) 01316 { 01317 memset(cmd.input + from, 0, MAX_LINE - from); 01318 cmd.cursor = from; 01319 } 01320 01321 static void cmd_execute(void) 01322 { 01323 if (strlen(cmd.input) != 0) { 01324 bool noduplicate = true; 01325 cmd_history_t *entry_ptr = cmd_history_find(0); 01326 if (entry_ptr) { 01327 if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) { 01328 noduplicate = false; 01329 } 01330 } 01331 if (noduplicate) { 01332 cmd_history_save(0); // new is saved to place 0 01333 cmd_history_save(-1); // new is created to the current one 01334 } 01335 } 01336 cmd.history = 0; 01337 01338 tr_deep("cmd_execute('%s') ", cmd.input); 01339 cmd_exe(cmd.input); 01340 cmd_line_clear(0); 01341 } 01342 01343 01344 static cmd_alias_t *alias_find(const char *alias) 01345 { 01346 cmd_alias_t *alias_ptr = NULL; 01347 if (alias == NULL || strlen(alias) == 0) { 01348 tr_error("alias_find invalid parameters"); 01349 return NULL; 01350 } 01351 01352 ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { 01353 if (strcmp(alias, cur_ptr->name_ptr) == 0) { 01354 alias_ptr = cur_ptr; 01355 break; 01356 } 01357 } 01358 return alias_ptr; 01359 } 01360 01361 static cmd_alias_t *alias_find_n(char *alias, int aliaslength, int n) 01362 { 01363 cmd_alias_t *alias_ptr = NULL; 01364 int i = 0; 01365 if (alias == NULL || strlen(alias) == 0) { 01366 tr_error("alias_find invalid parameters"); 01367 return NULL; 01368 } 01369 01370 ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { 01371 if (strncmp(alias, cur_ptr->name_ptr, aliaslength) == 0) { 01372 if (i == n) { 01373 alias_ptr = cur_ptr; 01374 break; 01375 } 01376 i++; 01377 } 01378 } 01379 return alias_ptr; 01380 } 01381 static cmd_variable_t *variable_find(char *variable) 01382 { 01383 cmd_variable_t *variable_ptr = NULL; 01384 if (variable == NULL || strlen(variable) == 0) { 01385 tr_error("variable_find invalid parameters"); 01386 return NULL; 01387 } 01388 01389 ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { 01390 if (strcmp(variable, cur_ptr->name_ptr) == 0) { 01391 variable_ptr = cur_ptr; 01392 break; 01393 } 01394 } 01395 return variable_ptr; 01396 } 01397 static cmd_variable_t *variable_find_n(char *variable, int length, int n) 01398 { 01399 cmd_variable_t *variable_ptr = NULL; 01400 if (variable == NULL || strlen(variable) == 0) { 01401 tr_error("variable_find invalid parameters"); 01402 return NULL; 01403 } 01404 int i = 0; 01405 ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { 01406 if (strncmp(variable, cur_ptr->name_ptr, length) == 0) { 01407 if (i == n) { 01408 variable_ptr = cur_ptr; 01409 break; 01410 } 01411 i++; 01412 } 01413 } 01414 return variable_ptr; 01415 } 01416 static void cmd_alias_print_all(void) 01417 { 01418 ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { 01419 if (cur_ptr->name_ptr != NULL) { 01420 cmd_printf("%-18s'%s'\r\n", cur_ptr->name_ptr, cur_ptr->value_ptr ? cur_ptr->value_ptr : ""); 01421 } 01422 } 01423 return; 01424 } 01425 static void cmd_variable_print_all(void) 01426 { 01427 ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { 01428 if (cur_ptr->name_ptr != NULL) { 01429 cmd_printf("%-18s'%s'\r\n", cur_ptr->name_ptr, cur_ptr->value_ptr ? cur_ptr->value_ptr : ""); 01430 } 01431 } 01432 return; 01433 } 01434 01435 void cmd_alias_add(const char *alias, const char *value) 01436 { 01437 cmd_alias_t *alias_ptr; 01438 if (alias == NULL || strlen(alias) == 0) { 01439 tr_warn("cmd_alias_add invalid parameters"); 01440 return; 01441 } 01442 alias_ptr = alias_find(alias); 01443 if (alias_ptr == NULL) { 01444 if (value == NULL) { 01445 return; // no need to add new null one 01446 } 01447 if (strlen(value) == 0) { 01448 return; // no need to add new empty one 01449 } 01450 alias_ptr = (cmd_alias_t *)MEM_ALLOC(sizeof(cmd_alias_t)); 01451 ns_list_add_to_end(&cmd.alias_list, alias_ptr); 01452 alias_ptr->name_ptr = (char *)MEM_ALLOC(strlen(alias) + 1); 01453 strcpy(alias_ptr->name_ptr, alias); 01454 alias_ptr->value_ptr = NULL; 01455 } 01456 if (value == NULL || strlen(value) == 0) { 01457 // delete this one 01458 ns_list_remove(&cmd.alias_list, alias_ptr); 01459 MEM_FREE(alias_ptr->name_ptr); 01460 MEM_FREE(alias_ptr->value_ptr); 01461 MEM_FREE(alias_ptr); 01462 } else { 01463 // add new or modify 01464 if (alias_ptr->value_ptr != NULL) { 01465 MEM_FREE(alias_ptr->value_ptr); 01466 } 01467 alias_ptr->value_ptr = (char *)MEM_ALLOC(strlen(value) + 1); 01468 strcpy(alias_ptr->value_ptr, value); 01469 } 01470 return; 01471 } 01472 void cmd_variable_add(char *variable, char *value) 01473 { 01474 cmd_variable_t *variable_ptr; 01475 01476 if (variable == NULL || strlen(variable) == 0) { 01477 tr_warn("cmd_variable_add invalid parameters"); 01478 return; 01479 } 01480 variable_ptr = variable_find(variable); 01481 if (variable_ptr == NULL) { 01482 if (value == NULL) { 01483 return; // adding null variable 01484 } 01485 if (strlen(value) == 0) { 01486 return; // no need to add new empty one 01487 } 01488 variable_ptr = (cmd_variable_t *)MEM_ALLOC(sizeof(cmd_variable_t)); 01489 ns_list_add_to_end(&cmd.variable_list, variable_ptr); 01490 variable_ptr->name_ptr = (char *)MEM_ALLOC(strlen(variable) + 1); 01491 strcpy(variable_ptr->name_ptr, variable); 01492 variable_ptr->value_ptr = NULL; 01493 } 01494 if (value == NULL || strlen(value) == 0) { 01495 // delete this one 01496 ns_list_remove(&cmd.variable_list, variable_ptr); 01497 MEM_FREE(variable_ptr->name_ptr); 01498 MEM_FREE(variable_ptr->value_ptr); 01499 MEM_FREE(variable_ptr); 01500 } else { 01501 // add new or modify 01502 if (variable_ptr->value_ptr != NULL) { 01503 MEM_FREE(variable_ptr->value_ptr); 01504 } 01505 variable_ptr->value_ptr = (char *)MEM_ALLOC(strlen(value) + 1); 01506 strcpy(variable_ptr->value_ptr, value); 01507 } 01508 return; 01509 } 01510 01511 static bool is_cmdline_commands(char *command) 01512 { 01513 if ((strncmp(command, "alias", 5) == 0) || 01514 (strcmp(command, "echo") == 0) || 01515 (strcmp(command, "set") == 0) || 01516 (strcmp(command, "clear") == 0) || 01517 (strcmp(command, "help") == 0)) { 01518 return true; 01519 } 01520 return false; 01521 } 01522 static void cmd_set_retfmt(char *fmt) 01523 { 01524 if (cmd.retcode_fmt) { 01525 MEM_FREE(cmd.retcode_fmt); 01526 } 01527 cmd.retcode_fmt = MEM_ALLOC(strlen(fmt) + 1); 01528 strcpy(cmd.retcode_fmt, fmt); 01529 } 01530 /*Basic commands for cmd line 01531 * alias 01532 * echo 01533 * set 01534 * clear 01535 * help 01536 */ 01537 int alias_command(int argc, char *argv[]) 01538 { 01539 if (argc == 1) { 01540 // print all alias 01541 cmd_printf("alias:\r\n"); 01542 cmd_alias_print_all(); 01543 } else if (argc == 2) { 01544 // print alias 01545 if (is_cmdline_commands(argv[1])) { 01546 cmd_printf("Cannot overwrite default commands with alias\r\n"); 01547 return -1; 01548 } 01549 tr_debug("Deleting alias %s", argv[1]); 01550 cmd_alias_add(argv[1], NULL); 01551 } else { 01552 // set alias 01553 tr_debug("Setting alias %s = %s", argv[1], argv[2]); 01554 cmd_alias_add(argv[1], argv[2]); 01555 } 01556 return 0; 01557 } 01558 int set_command(int argc, char *argv[]) 01559 { 01560 if (argc == 1) { 01561 // print all alias 01562 cmd_printf("variables:\r\n"); 01563 cmd_variable_print_all(); 01564 } else if (argc == 2) { 01565 // print alias 01566 tr_debug("Deleting variable %s", argv[1]); 01567 cmd_variable_add(argv[1], NULL); 01568 } else { 01569 // set alias 01570 tr_debug("Setting variable %s = %s", argv[1], argv[2]); 01571 //handle special cases: vt100 on|off 01572 bool state; 01573 if (cmd_parameter_bool(argc, argv, "--vt100", &state)) { 01574 cmd.vt100_on = state; 01575 return 0; 01576 } 01577 if (cmd_parameter_bool(argc, argv, "--retcode", &state)) { 01578 cmd.print_retcode = state; 01579 return 0; 01580 } 01581 char *str; 01582 if (cmd_parameter_val(argc, argv, "--retfmt", &str)) { 01583 cmd_set_retfmt(str); 01584 return 0; 01585 } 01586 cmd_variable_add(argv[1], argv[2]); 01587 } 01588 return 0; 01589 } 01590 int echo_command(int argc, char *argv[]) 01591 { 01592 bool printEcho = false; 01593 if (argc == 1) { 01594 printEcho = true; 01595 } else if (argc == 2) { 01596 if (strcmp(argv[1], "off") == 0) { 01597 cmd_echo(false); 01598 printEcho = true; 01599 } else if (strcmp(argv[1], "on") == 0) { 01600 cmd_echo(true); 01601 printEcho = true; 01602 } 01603 } 01604 if( printEcho ) { 01605 cmd_printf("ECHO is %s\r\n", cmd.echo ? "on" : "off"); 01606 } else { 01607 for (int n = 1; n < argc; n++) { 01608 tr_deep("ECHO: %s\r\n", argv[n]); 01609 cmd_printf("%s ", argv[n]); 01610 } 01611 cmd_printf("\r\n"); 01612 } 01613 return 0; 01614 } 01615 01616 int clear_command(int argc, char *argv[]) 01617 { 01618 (void)argc; 01619 (void )argv; 01620 01621 cmd_echo(true); 01622 cmd_init_screen(); 01623 return 0; 01624 } 01625 int help_command(int argc, char *argv[]) 01626 { 01627 cmd_printf("Commands:\r\n"); 01628 if (argc == 1) { 01629 ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) { 01630 cmd_printf("%-16s%s\r\n", cur_ptr->name_ptr, (cur_ptr->info_ptr ? cur_ptr->info_ptr : "")); 01631 } 01632 } else if (argc == 2) { 01633 cmd_command_t *cmd_ptr = cmd_find(argv[1]); 01634 if (cmd_ptr) { 01635 cmd_printf("Command: %s\r\n", cmd_ptr->name_ptr); 01636 if (cmd_ptr->man_ptr) { 01637 cmd_printf("%s\r\n", cmd_ptr->man_ptr); 01638 } else if (cmd_ptr->info_ptr) { 01639 cmd_printf("%s\r\n", cmd_ptr->info_ptr); 01640 } 01641 } else { 01642 cmd_printf("Command '%s' not found", argv[1]); 01643 } 01644 } 01645 return 0; 01646 } 01647 int history_command(int argc, char *argv[]) 01648 { 01649 if (argc == 1) { 01650 cmd_printf("History [%i/%i]:\r\n", (int)ns_list_count(&cmd.history_list), cmd.history_max_count); 01651 int i = 0; 01652 ns_list_foreach_reverse(cmd_history_t, cur_ptr, &cmd.history_list) { 01653 cmd_printf("[%i]: %s\r\n", i++, cur_ptr->command_ptr); 01654 } 01655 } else if (argc == 2) { 01656 if (strcmp(argv[1], "clear") == 0) { 01657 cmd_history_clean(); 01658 } else { 01659 cmd_history_size(strtoul(argv[1], 0, 10)); 01660 } 01661 } 01662 return 0; 01663 } 01664 01665 /** Parameter helping functions 01666 */ 01667 int cmd_parameter_index(int argc, char *argv[], const char *key) 01668 { 01669 int i = 0; 01670 for (i = 1; i < argc; i++) { 01671 if (strcmp(argv[i], key) == 0) { 01672 return i; 01673 } 01674 } 01675 return -1; 01676 } 01677 bool cmd_has_option(int argc, char *argv[], const char *key) 01678 { 01679 int i = 0; 01680 for (i = 1; i < argc; i++) { 01681 if (argv[i][0] == '-' && argv[i][1] != '-') { 01682 if (strstr(argv[i], key) != 0) { 01683 return true; 01684 } 01685 } 01686 } 01687 return false; 01688 } 01689 bool cmd_parameter_bool(int argc, char *argv[], const char *key, bool *value) 01690 { 01691 int i = cmd_parameter_index(argc, argv, key); 01692 if (i > 0) { 01693 if (argc > (i + 1)) { 01694 if (strcmp(argv[i + 1], "on") == 0 || 01695 strcmp(argv[i + 1], "1") == 0 || 01696 strcmp(argv[i + 1], "true") == 0 || 01697 strcmp(argv[i + 1], "enable") == 0 || 01698 strcmp(argv[i + 1], "allow") == 0) { 01699 *value = true; 01700 } else { 01701 *value = false; 01702 } 01703 return true; 01704 } 01705 } 01706 return false; 01707 } 01708 bool cmd_parameter_val(int argc, char *argv[], const char *key, char **value) 01709 { 01710 int i = cmd_parameter_index(argc, argv, key); 01711 if (i > 0) { 01712 if (argc > (i + 1)) { 01713 *value = argv[i + 1]; 01714 return true; 01715 } 01716 } 01717 return false; 01718 } 01719 bool cmd_parameter_int(int argc, char *argv[], const char *key, int32_t *value) 01720 { 01721 int i = cmd_parameter_index(argc, argv, key); 01722 char* tailptr; 01723 if (i > 0) { 01724 if (argc > (i + 1)) { 01725 *value = strtol(argv[i + 1], &tailptr, 10); 01726 if (0 == *tailptr) { 01727 return true; 01728 } 01729 if (!isspace((unsigned char) *tailptr)) { 01730 return false; 01731 } else { 01732 return true; 01733 } 01734 } 01735 } 01736 return false; 01737 } 01738 bool cmd_parameter_float(int argc, char *argv[], const char *key, float *value) 01739 { 01740 int i = cmd_parameter_index(argc, argv, key); 01741 char* tailptr; 01742 if (i > 0) { 01743 if (argc > (i + 1)) { 01744 *value = strtof(argv[i + 1], &tailptr); 01745 if (0 == *tailptr) { 01746 return true; //Should be correct read always 01747 } 01748 if (!isspace((unsigned char) *tailptr)) { 01749 return false; //Garbage in tailptr 01750 } else { 01751 return true; //Spaces are fine after float 01752 } 01753 } 01754 } 01755 return false; 01756 } 01757 // convert hex string (eg. "76 ab ff") to binary array 01758 static int string_to_bytes(const char *str, uint8_t *buf, int bytes) 01759 { 01760 int len = strlen(str); 01761 if( len <= (3*bytes - 1)) { 01762 int i; 01763 for(i=0;i<bytes;i++){ 01764 if( i*3<len ){ 01765 buf[i] = (uint8_t)strtoul(str+i*3, 0, 16); 01766 } else { 01767 buf[i] = 0; 01768 } 01769 } 01770 return 0; 01771 } 01772 return -1; 01773 } 01774 01775 static uint64_t read_64_bit(const uint8_t data_buf[__static 8]) 01776 { 01777 uint64_t temp_64; 01778 temp_64 = (uint64_t)(*data_buf++) << 56; 01779 temp_64 += (uint64_t)(*data_buf++) << 48; 01780 temp_64 += (uint64_t)(*data_buf++) << 40; 01781 temp_64 += (uint64_t)(*data_buf++) << 32; 01782 temp_64 += (uint64_t)(*data_buf++) << 24; 01783 temp_64 += (uint64_t)(*data_buf++) << 16; 01784 temp_64 += (uint64_t)(*data_buf++) << 8; 01785 temp_64 += *data_buf++; 01786 return temp_64; 01787 } 01788 01789 bool cmd_parameter_timestamp(int argc, char *argv[], const char *key, int64_t *value) 01790 { 01791 int i = cmd_parameter_index(argc, argv, key); 01792 if (i > 0) { 01793 if (argc > (i + 1)) { 01794 if (strchr(argv[i + 1],',') != 0) { 01795 // Format seconds,tics 01796 const char splitValue[] = ", "; 01797 char *token; 01798 token = strtok(argv[i + 1], splitValue); 01799 if (token) { 01800 *value = strtoul(token, 0, 10) << 16; 01801 } 01802 token = strtok(NULL, splitValue); 01803 if (token) { 01804 *value |= (0xffff & strtoul(token, 0, 10)); 01805 } 01806 } else if (strchr(argv[i + 1],':') != 0 ) { 01807 // Format 00:00:00:00:00:00:00:00 01808 uint8_t buf[8]; 01809 if (string_to_bytes(argv[i + 1], buf, 8) == 0) { 01810 *value = read_64_bit(buf); 01811 } else { 01812 cmd_printf("timestamp should be 8 bytes long\r\n"); 01813 } 01814 } else { 01815 // Format uint64 01816 *value = strtol(argv[i + 1], 0, 10); 01817 } 01818 return true; 01819 } 01820 } 01821 return false; 01822 } 01823 char *cmd_parameter_last(int argc, char *argv[]) 01824 { 01825 if (argc > 1) { 01826 return argv[ argc - 1 ]; 01827 } 01828 return NULL; 01829 }
Generated on Tue Aug 9 2022 00:37:16 by
