Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ns_cmdline.c Source File

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(MBED_CONF_RTOS_PRESENT) || defined(__APPLE__)
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 // #define MBED_CLIENT_CLI_TRACE_ENABLE
00063 // MBED_CLIENT_CLI_TRACE_ENABLE is to enable the traces for debugging,
00064 // By default all debug traces are disabled.
00065 #ifndef MBED_CLIENT_CLI_TRACE_ENABLE
00066 #undef tr_error
00067 #define tr_error(...)
00068 #undef tr_warn
00069 #define tr_warn(...)
00070 #undef tr_debug
00071 #define tr_debug(...)
00072 #undef tr_info
00073 #define tr_info(...)
00074 #endif
00075 
00076 #ifdef TRACE_DEEP
00077 #define tr_deep   tr_debug
00078 #else
00079 #define tr_deep(...)
00080 #endif
00081 
00082 #define TRACE_GROUP "cmdL"
00083 
00084 #ifndef MBED_CMDLINE_BOOT_MESSAGE
00085 #define MBED_CMDLINE_BOOT_MESSAGE "ARM Ltd\r\n"
00086 #endif
00087 #define ESCAPE(x) "\x1b" x
00088 #define CR_S "\r"
00089 #define LF_S "\n"
00090 #define CLEAR_ENTIRE_LINE ESCAPE("[2K")
00091 #define CLEAR_ENTIRE_SCREEN ESCAPE("[2J")
00092 #define ENABLE_AUTO_WRAP_MODE ESCAPE("[7h")
00093 #define MOVE_CURSOR_LEFT_N_CHAR ESCAPE("[%dD")
00094 
00095 #define SET_TOP_AND_BOTTOM_LINES ESCAPE("[;r")
00096 #define MOVE_CURSOR_TO_BOTTOM_RIGHT ESCAPE("[999;999H")
00097 #define STORE_CURSOR_POS ESCAPE("7")
00098 #define RESTORE_CURSOR_POS ESCAPE("8")
00099 #define GET_CURSOR_POSITION ESCAPE("[6n")
00100 
00101 #define REQUEST_SCREEN_SIZE STORE_CURSOR_POS SET_TOP_AND_BOTTOM_LINES MOVE_CURSOR_TO_BOTTOM_RIGHT GET_CURSOR_POSITION RESTORE_CURSOR_POS
00102 
00103 /*ASCII defines*/
00104 #define ESC 0x1B
00105 #define DEL 0x7F
00106 #define BS  0x08
00107 #define ETX 0x03
00108 #define ETB 0x17
00109 #define TAB 0x09
00110 #define CAN 0x18
00111 
00112 #define DEFAULT_RETFMT "retcode: %i\r\n"
00113 #define DEFAULT_PROMPT "/>"
00114 #define VAR_PROMPT "PS1"
00115 #define VAR_RETFMT "RETFMT"
00116 
00117 // Maximum length of input line
00118 #ifdef MBED_CMDLINE_MAX_LINE_LENGTH
00119 #define MAX_LINE MBED_CMDLINE_MAX_LINE_LENGTH
00120 #else
00121 #define MAX_LINE 2000
00122 #endif
00123 // Maximum number of arguments in a single command
00124 #ifdef MBED_CMDLINE_ARGUMENTS_MAX_COUNT
00125 #define MAX_ARGUMENTS MBED_CMDLINE_ARGUMENTS_MAX_COUNT
00126 #else
00127 #define MAX_ARGUMENTS 30
00128 #endif
00129 // Maximum number of commands saved in history
00130 #ifdef MBED_CMDLINE_HISTORY_MAX_COUNT
00131 #define HISTORY_MAX_COUNT MBED_CMDLINE_HISTORY_MAX_COUNT
00132 #else
00133 #define HISTORY_MAX_COUNT 32
00134 #endif
00135 //include manuals or not (save memory a little when not include)
00136 #ifndef MBED_CMDLINE_INCLUDE_MAN
00137 #define MBED_CMDLINE_INCLUDE_MAN 1
00138 #endif
00139 
00140 
00141 typedef struct cmd_history_s {
00142     char *command_ptr;
00143     ns_list_link_t link;
00144 } cmd_history_t;
00145 
00146 typedef struct cmd_command_s {
00147     const char *name_ptr;
00148     const char *info_ptr;
00149     const char *man_ptr;
00150     cmd_run_cb *run_cb;
00151     bool        busy;
00152     ns_list_link_t link;
00153 } cmd_command_t;
00154 
00155 typedef struct cmd_alias_s {
00156     char *name_ptr;
00157     char *value_ptr;
00158     ns_list_link_t link;
00159 } cmd_alias_t;
00160 
00161 union Data {
00162     char *ptr;
00163     int i;
00164 };
00165 typedef enum value_type_s {
00166     VALUE_TYPE_STR,
00167     VALUE_TYPE_INT
00168 } value_type_t;
00169 
00170 typedef struct cmd_variable_s {
00171     char *name_ptr;
00172     union Data value;
00173     value_type_t type;
00174     ns_list_link_t link;
00175 } cmd_variable_t;
00176 
00177 typedef enum operator_s {
00178     OPERATOR_SEMI_COLON,  //default
00179     OPERATOR_AND,
00180     OPERATOR_OR,
00181     OPERATOR_BACKGROUND,
00182     OPERATOR_PIPE
00183 } operator_t;
00184 typedef struct cmd_exe_s {
00185     char          *cmd_s;
00186     operator_t     operator;
00187     ns_list_link_t link;
00188 } cmd_exe_t;
00189 typedef NS_LIST_HEAD (cmd_exe_t, link) cmd_list_t;
00190 typedef NS_LIST_HEAD (cmd_history_t, link) history_list_t;
00191 typedef NS_LIST_HEAD (cmd_command_t, link) command_list_t;
00192 typedef NS_LIST_HEAD (cmd_alias_t, link) alias_list_t;
00193 typedef NS_LIST_HEAD (cmd_variable_t, link) variable_list_t;
00194 
00195 typedef struct cmd_class_s {
00196     char input[MAX_LINE];             // input data
00197     char escape[10];                  // escape data
00198     int16_t history;                  // history position
00199     int16_t cursor;                   // cursor position
00200     int16_t escape_index;             // escape index
00201     history_list_t history_list;      // input history
00202     uint8_t history_max_count;        // history max size
00203     command_list_t command_list;      // commands list
00204     alias_list_t alias_list;          // alias list
00205     variable_list_t variable_list;    // variables list
00206     bool vt100_on;                    // control characters
00207     bool init;                        // true when lists are initialized already
00208     bool escaping;                    // escaping input
00209     bool insert;                      // insert enabled
00210     int  tab_lookup;                  // originally lookup characters count
00211     int  tab_lookup_cmd_n;            // index in command list
00212     int  tab_lookup_n;                //
00213     bool prev_cr;                     // indicate if cr was last char
00214     bool echo;                        // echo inputs
00215     cmd_ready_cb_f *ready_cb;         // ready cb function
00216     cmd_list_t  cmd_buffer;
00217     cmd_exe_t  *cmd_buffer_ptr;
00218     cmd_command_t  *cmd_ptr;
00219     int8_t      tasklet_id;
00220     int8_t      network_tasklet_id;
00221     bool        idle;
00222 
00223     cmd_print_t *out;                  // print cb function
00224     void (*ctrl_fnc)(uint8_t c);      // control cb function
00225     void (*mutex_wait_fnc)(void);         // mutex wait cb function
00226     void (*mutex_release_fnc)(void);      // mutex release cb function
00227     input_passthrough_func_t passthrough_fnc; // input passthrough cb function
00228 } cmd_class_t;
00229 
00230 cmd_class_t cmd = {
00231     .init = false,
00232     .cmd_ptr = NULL,
00233     .mutex_wait_fnc = NULL,
00234     .mutex_release_fnc = NULL,
00235     .passthrough_fnc = NULL
00236 };
00237 
00238 /* Function prototypes
00239  */
00240 static void             cmd_init_base_commands(void);
00241 static void             cmd_replace_alias(char *input);
00242 static void             cmd_replace_variables(char *input);
00243 static int              cmd_parse_argv(char *string_ptr, char **argv);
00244 static void             cmd_execute(void);
00245 static void             cmd_line_clear(int from);
00246 static void             cmd_history_save(int16_t index);
00247 static void             cmd_history_get(uint16_t index);
00248 static void             cmd_history_clean_overflow(void);
00249 static void             cmd_history_clean(void);
00250 static void             cmd_echo(bool on);
00251 static cmd_history_t   *cmd_history_find(int16_t index);
00252 static bool             cmd_tab_lookup(void);
00253 static void             cmd_clear_last_word(void);
00254 static void             cmd_move_cursor_to_last_space(void);
00255 static void             cmd_move_cursor_to_next_space(void);
00256 static const char      *cmd_input_lookup(char *name, int namelength, int n);
00257 static char            *cmd_input_lookup_var(char *name, int namelength, int n);
00258 static cmd_command_t   *cmd_find(const char *name);
00259 static cmd_command_t   *cmd_find_n(char *name, int nameLength, int n);
00260 static cmd_alias_t     *alias_find(const char *alias);
00261 static cmd_alias_t     *alias_find_n(char *alias, int aliaslength, int n);
00262 static cmd_variable_t  *variable_find(char *variable);
00263 static cmd_variable_t  *variable_find_n(char *variable, int length, int n);
00264 static void             cmd_print_man(cmd_command_t *command_ptr);
00265 static void             goto_end_of_history(void);
00266 static void             goto_beginning_of_history(void);
00267 static void             cmd_set_input(const char *str, int cur);
00268 static char            *next_command(char *string_ptr, operator_t *mode);
00269 /** Run single command through cmd intepreter
00270  * \param string_ptr    command string with parameters
00271  * \ret  command return code (CMDLINE_RETCODE_*)
00272  */
00273 static int              cmd_run(char *string_ptr);
00274 static cmd_exe_t       *cmd_next_ptr(int retcode);
00275 static void             cmd_split(char *string_ptr);
00276 static void             cmd_push(char *cmd_str, operator_t oper);
00277 
00278 /*internal shell commands
00279  */
00280 int true_command(int argc, char *argv[]);
00281 int false_command(int argc, char *argv[]);
00282 int echo_command(int argc, char *argv[]);
00283 int alias_command(int argc, char *argv[]);
00284 int unset_command(int argc, char *argv[]);
00285 int set_command(int argc, char *argv[]);
00286 int clear_command(int argc, char *argv[]);
00287 int help_command(int argc, char *argv[]);
00288 int history_command(int argc, char *argv[]);
00289 
00290 /** Internal helper functions
00291  */
00292 static const char *find_last_space(const char *from, const char *to);
00293 static int replace_string(
00294     char *str, int str_len,
00295     const char *old_str, const char *new_str);
00296 
00297 void default_cmd_response_out(const char *fmt, va_list ap)
00298 {
00299     vprintf(fmt, ap);
00300     fflush(stdout);
00301 }
00302 void cmd_printf(const char *fmt, ...)
00303 {
00304     va_list ap;
00305     va_start(ap, fmt);
00306     cmd_vprintf(fmt, ap);
00307     va_end(ap);
00308 }
00309 void cmd_vprintf(const char *fmt, va_list ap)
00310 {
00311     if (cmd.mutex_wait_fnc) {
00312         cmd.mutex_wait_fnc();
00313     }
00314     cmd.out(fmt, ap);
00315     if (cmd.mutex_release_fnc) {
00316         cmd.mutex_release_fnc();
00317     }
00318 }
00319 /* Function definitions
00320  */
00321 void cmd_init(cmd_print_t *outf)
00322 {
00323     if (!cmd.init) {
00324         ns_list_init(&cmd.alias_list);
00325         ns_list_init(&cmd.history_list);
00326         ns_list_init(&cmd.command_list);
00327         ns_list_init(&cmd.variable_list);
00328         ns_list_init(&cmd.cmd_buffer);
00329         cmd.init = true;
00330     }
00331     cmd.out = outf ? outf : default_cmd_response_out;
00332     cmd.ctrl_fnc = NULL;
00333     cmd.echo = true;
00334     cmd.escaping = false;
00335     cmd.insert = true;
00336     cmd.cursor = 0;
00337     cmd.prev_cr = false;
00338     cmd.vt100_on = true;
00339     cmd.history_max_count = HISTORY_MAX_COUNT;
00340     cmd.tab_lookup = 0;
00341     cmd.tab_lookup_cmd_n = 0;
00342     cmd.tab_lookup_n = 0;
00343     cmd.cmd_buffer_ptr = 0;
00344     cmd.idle = true;
00345     cmd.ready_cb = cmd_next;
00346     cmd.passthrough_fnc = NULL;
00347     cmd_variable_add(VAR_PROMPT, DEFAULT_PROMPT);
00348     cmd_variable_add_int("?", 0);
00349     //cmd_alias_add("auto-on", "set PS1=\r\nretcode=$?\r\n&&echo off");
00350     //cmd_alias_add("auto-off", "set PS1="DEFAULT_PROMPT"&&echo on");
00351     cmd_line_clear(0);            // clear line
00352     cmd_history_save(0);          // the current line is the 0 item
00353     cmd_init_base_commands();
00354     cmd_init_screen();
00355     return;
00356 }
00357 void cmd_request_screen_size(void)
00358 {
00359     cmd_printf(REQUEST_SCREEN_SIZE);
00360 }
00361 
00362 const char *cmdline_get_prompt(void)
00363 {
00364     cmd_variable_t *var_ptr = variable_find(VAR_PROMPT);
00365     return var_ptr && var_ptr->type == VALUE_TYPE_STR ? var_ptr->value.ptr : "";
00366 }
00367 const char *cmd_get_retfmt(void)
00368 {
00369     cmd_variable_t *var_ptr = variable_find(VAR_RETFMT);
00370     return var_ptr && var_ptr->type == VALUE_TYPE_STR ? var_ptr->value.ptr : 0;
00371 }
00372 
00373 #if MBED_CMDLINE_INCLUDE_MAN == 1
00374 #define MAN_ECHO    "Displays messages, or turns command echoing on or off\r\n"\
00375                     "echo <data_to_be_print>\r\n"\
00376                     "some special parameters:\r\n"\
00377                     "<bool>                 On/Off echo input characters\r\n"
00378 #define MAN_ALIAS   "alias <theAlias> <command (parameters)>\r\n"
00379 #define MAN_UNSET   "unset <var_name>\r\n"
00380 #define MAN_SET     "set <var_name> <value>\r\n"\
00381                     "some special parameters\r\n"\
00382                     "--vt100 <bool>         On/Off vt100 controls\r\n"\
00383                     "--retcode <bool>       On/Off retcode print after execution\r\n"\
00384                     "--retfmt <format>      Return print format. Default: \"retcode: %i\\n\"\r\n"
00385 
00386 #define MAN_CLEAR   "Clears the display"
00387 #define MAN_HISTORY "Show commands history\r\n"\
00388                     "history (<optio>)\r\n"\
00389                     "clear                  Clear history\r\n"
00390 #else
00391 #define MAN_ECHO    NULL
00392 #define MAN_ALIAS   NULL
00393 #define MAN_SET     NULL
00394 #define MAN_CLEAR   NULL
00395 #define MAN_HISTORY NULL
00396 #endif
00397 
00398 static void cmd_init_base_commands(void)
00399 {
00400     cmd_add("help",     help_command,     "This help",            NULL);
00401     cmd_add("echo",     echo_command,     "Echo controlling",     MAN_ECHO);
00402     cmd_add("alias",    alias_command,    "Handle aliases",       MAN_ALIAS);
00403     cmd_add("unset",    unset_command,    "unset variables",      MAN_UNSET);
00404     cmd_add("set",      set_command,      "print or set variables", MAN_SET);
00405     cmd_add("clear",    clear_command,    "Clears the display",   MAN_CLEAR);
00406     cmd_add("history",  history_command,  "View your command Line History", MAN_HISTORY);
00407     cmd_add("true",     true_command, 0, 0);
00408     cmd_add("false",    false_command, 0, 0);
00409 }
00410 void cmd_reset(void)
00411 {
00412     cmd_free();
00413     cmd_init_base_commands();
00414 }
00415 void cmd_free(void)
00416 {
00417     ns_list_foreach_safe(cmd_command_t, cur_ptr, &cmd.command_list) {
00418         cmd_delete(cur_ptr->name_ptr);
00419     }
00420     ns_list_foreach_safe(cmd_alias_t, cur_ptr, &cmd.alias_list) {
00421         cmd_alias_add(cur_ptr->name_ptr, NULL);
00422     }
00423     ns_list_foreach_safe(cmd_variable_t, cur_ptr, &cmd.variable_list) {
00424         if (cur_ptr->type == VALUE_TYPE_STR) {
00425             cmd_variable_add(cur_ptr->value.ptr, NULL);
00426         }
00427     }
00428     ns_list_foreach_safe(cmd_history_t, cur_ptr, &cmd.history_list) {
00429         MEM_FREE(cur_ptr->command_ptr);
00430         ns_list_remove(&cmd.history_list, cur_ptr);
00431         MEM_FREE(cur_ptr);
00432     }
00433     cmd.mutex_wait_fnc = NULL;
00434     cmd.mutex_release_fnc = NULL;
00435 }
00436 
00437 void cmd_input_passthrough_func(input_passthrough_func_t passthrough_fnc)
00438 {
00439     cmd.passthrough_fnc = passthrough_fnc;
00440 }
00441 
00442 void cmd_exe(char *str)
00443 {
00444     cmd_split(str);
00445     if (cmd.cmd_buffer_ptr == 0) {
00446         //execution buffer is empty
00447         cmd.idle = false; //not really, but fake it
00448         cmd_ready(CMDLINE_RETCODE_SUCCESS);
00449     } else {
00450         tr_debug("previous cmd is still in progress");
00451     }
00452 }
00453 void cmd_set_ready_cb(cmd_ready_cb_f *cb)
00454 {
00455     cmd.ready_cb = cb;
00456 }
00457 void cmd_ready(int retcode)
00458 {
00459     if (cmd.cmd_ptr && cmd.cmd_ptr->busy) {
00460         //execution finished
00461         cmd.cmd_ptr->busy = false;
00462     }
00463     if (!cmd.idle) {
00464         if (cmd.cmd_buffer_ptr == NULL) {
00465             tr_debug("goto next command");
00466         } else {
00467             tr_debug("cmd '%s' executed with retcode: %i", cmd.cmd_buffer_ptr->cmd_s, retcode);
00468         }
00469         if (cmd.ready_cb == NULL) {
00470             tr_warn("Missing ready_cb! use cmd_set_ready_cb()");
00471         } else {
00472             cmd.ready_cb(retcode);
00473         }
00474     } else {
00475         tr_warn("Someone call cmd_ready(%i) even there shouldn't be any running cmd", retcode);
00476         if (cmd.echo) {
00477             cmd_output();    //refresh if this happens
00478         }
00479     }
00480 }
00481 void cmd_next(int retcode)
00482 {
00483     cmd.idle = true;
00484     //figure out next command
00485     cmd.cmd_buffer_ptr = cmd_next_ptr(retcode);
00486     if (cmd.cmd_buffer_ptr) {
00487         cmd.idle = false;
00488         //yep there was some -> lets execute it
00489         retcode = cmd_run(cmd.cmd_buffer_ptr->cmd_s);
00490         //check if execution goes to the backend or not
00491         if (retcode == CMDLINE_RETCODE_EXCUTING_CONTINUE) {
00492             if ((NULL != cmd.cmd_buffer_ptr) && cmd.cmd_buffer_ptr->operator == OPERATOR_BACKGROUND) {
00493                 //execution continue in background, but operator say that it's "ready"
00494                 cmd_ready(CMDLINE_RETCODE_SUCCESS);
00495             } else {
00496                 //command execution phase continuous in background
00497                 tr_debug("Command execution continuous in background..");
00498             }
00499         } else {
00500             //execution finished -> call ready function with retcode
00501             cmd_ready(retcode);
00502         }
00503     } else {
00504         const char *retfmt = cmd_get_retfmt();
00505         if (retfmt) {
00506             cmd_printf(retfmt, retcode);
00507         }
00508         cmd_line_clear(0);
00509         if (cmd.echo) {
00510             cmd_output();    //ready
00511         }
00512     }
00513 }
00514 static cmd_exe_t *cmd_pop(void)
00515 {
00516     cmd_exe_t *cmd_ptr = ns_list_get_first(&cmd.cmd_buffer),
00517                *next_cmd = ns_list_get_next(&cmd.cmd_buffer, cmd_ptr);
00518 
00519     if (cmd.cmd_buffer_ptr == 0) {
00520         //was first in bool
00521         next_cmd = ns_list_get_first(&cmd.cmd_buffer);
00522     } else {
00523         MEM_FREE(cmd_ptr->cmd_s);
00524         ns_list_remove(&cmd.cmd_buffer, cmd_ptr);
00525         MEM_FREE(cmd_ptr);
00526     }
00527     return next_cmd;
00528 }
00529 
00530 static cmd_exe_t *cmd_next_ptr(int retcode)
00531 {
00532     cmd_exe_t *next_cmd = cmd.cmd_buffer_ptr;
00533     if (ns_list_is_empty(&cmd.cmd_buffer)) {
00534         return NULL;
00535     }
00536     if (cmd.cmd_buffer_ptr == NULL) {
00537         return cmd_pop();
00538     }
00539     switch (cmd.cmd_buffer_ptr->operator) {
00540         case (OPERATOR_AND):
00541             if (retcode != CMDLINE_RETCODE_SUCCESS) {
00542                 //if fails, go to next command, which not have AND operator
00543                 while ((next_cmd->operator == OPERATOR_AND) && ((next_cmd = cmd_pop()) != 0));
00544             } else {
00545                 next_cmd = cmd_pop();
00546             }
00547             break;
00548         case (OPERATOR_OR):
00549             if (retcode == CMDLINE_RETCODE_SUCCESS) {
00550                 //if fails, go to next command, which not have OR operator
00551                 while ((next_cmd->operator == OPERATOR_OR) && ((next_cmd = cmd_pop()) != 0));
00552             } else {
00553                 next_cmd = cmd_pop();
00554             }
00555             break;
00556         case (OPERATOR_BACKGROUND):
00557             next_cmd = cmd_pop();
00558             break;
00559         case (OPERATOR_PIPE):
00560             cmd_printf("pipe is not supported\r\n");
00561             while ((next_cmd = cmd_pop()) != 0);
00562             break;
00563         case (OPERATOR_SEMI_COLON):
00564         default:
00565             //get next command to be execute (might be null if there is no more)
00566             next_cmd = cmd_pop();
00567             break;
00568     }
00569 
00570     //return next command if any
00571     return next_cmd;
00572 }
00573 static void cmd_split(char *string_ptr)
00574 {
00575     char *ptr = string_ptr, *next;
00576     operator_t oper = OPERATOR_SEMI_COLON;
00577     do {
00578         next = next_command(ptr, &oper);
00579         cmd_push(ptr, oper);
00580         ptr = next;
00581         if (next && !*next) {
00582             break;
00583         }
00584     } while (ptr != 0);
00585 }
00586 
00587 static void cmd_push(char *cmd_str, operator_t oper)
00588 {
00589     //store this command to the stack
00590     cmd_exe_t *cmd_ptr = MEM_ALLOC(sizeof(cmd_exe_t));
00591     if (cmd_ptr == NULL) {
00592         tr_error("mem alloc failed in cmd_push");
00593         return;
00594     }
00595     cmd_ptr->cmd_s = MEM_ALLOC(strlen(cmd_str) + 1);
00596     if (cmd_ptr->cmd_s == NULL) {
00597         MEM_FREE(cmd_ptr);
00598         tr_error("mem alloc failed in cmd_push cmd_s");
00599         return;
00600     }
00601     strcpy(cmd_ptr->cmd_s, cmd_str);
00602     cmd_ptr->operator = oper;
00603     ns_list_add_to_end(&cmd.cmd_buffer, cmd_ptr);
00604 }
00605 void cmd_out_func(cmd_print_t *outf)
00606 {
00607     cmd.out = outf;
00608 }
00609 void cmd_ctrl_func(void (*sohf)(uint8_t c))
00610 {
00611     cmd.ctrl_fnc = sohf;
00612 }
00613 
00614 void cmd_mutex_wait_func(void (*mutex_wait_f)(void))
00615 {
00616     cmd.mutex_wait_fnc = mutex_wait_f;
00617 }
00618 void cmd_mutex_release_func(void (*mutex_release_f)(void))
00619 {
00620     cmd.mutex_release_fnc = mutex_release_f;
00621 }
00622 
00623 void cmd_mutex_lock()
00624 {
00625     if (cmd.mutex_wait_fnc) {
00626         cmd.mutex_wait_fnc();
00627     }
00628 }
00629 
00630 void cmd_mutex_unlock()
00631 {
00632     if (cmd.mutex_release_fnc) {
00633         cmd.mutex_release_fnc();
00634     }
00635 }
00636 void cmd_init_screen()
00637 {
00638     if (cmd.vt100_on) {
00639         cmd_printf(CR_S CLEAR_ENTIRE_SCREEN); /* Clear screen */
00640         cmd_printf(ENABLE_AUTO_WRAP_MODE); /* enable line wrap */
00641     }
00642     cmd_printf(MBED_CMDLINE_BOOT_MESSAGE);
00643     cmd_output();
00644 }
00645 uint8_t cmd_history_size(uint8_t max)
00646 {
00647     if (max > 0) {
00648         cmd.history_max_count = max;
00649         cmd_history_clean_overflow();
00650     }
00651     return cmd.history_max_count;
00652 }
00653 static void cmd_echo(bool on)
00654 {
00655     cmd.echo = on;
00656 }
00657 bool cmd_echo_state(void)
00658 {
00659     return cmd.echo;
00660 }
00661 static cmd_command_t *cmd_find_n(char *name, int nameLength, int n)
00662 {
00663     cmd_command_t *cmd_ptr = NULL;
00664     if (name != NULL && nameLength != 0) {
00665         int i = 0;
00666         ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) {
00667             if (strncmp(name, cur_ptr->name_ptr, nameLength) == 0) {
00668                 if (i == n) {
00669                     cmd_ptr = cur_ptr;
00670                     break;
00671                 }
00672                 i++;
00673             }
00674         }
00675     }
00676     return cmd_ptr;
00677 }
00678 static const char *cmd_input_lookup(char *name, int namelength, int n)
00679 {
00680     const char *str = NULL;
00681     cmd_command_t *cmd_ptr = cmd_find_n(name, namelength, n);
00682     if (cmd_ptr) {
00683         str = cmd_ptr->name_ptr;
00684         cmd.tab_lookup_n = n + 1;
00685     } else {
00686         n -= cmd.tab_lookup_n;
00687         cmd_alias_t *alias = alias_find_n(name, namelength, n);
00688         if (alias) {
00689             str = (const char *)alias->name_ptr;
00690         }
00691     }
00692 
00693     return str;
00694 }
00695 static char *cmd_input_lookup_var(char *name, int namelength, int n)
00696 {
00697     char *str = NULL;
00698     cmd_variable_t *var = variable_find_n(name, namelength, n);
00699     if (var) {
00700         str = var->name_ptr;
00701     }
00702     return str;
00703 }
00704 static cmd_command_t *cmd_find(const char *name)
00705 {
00706     cmd_command_t *cmd_ptr = NULL;
00707     if (name == NULL || strlen(name) == 0) {
00708         tr_error("cmd_find invalid parameters");
00709         return NULL;
00710     }
00711 
00712     ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) {
00713         if (strcmp(name, cur_ptr->name_ptr) == 0) {
00714             cmd_ptr = cur_ptr;
00715             break;
00716         }
00717     }
00718     return cmd_ptr;
00719 }
00720 
00721 void cmd_add(const char *name, cmd_run_cb *callback, const char *info, const char *man)
00722 {
00723     cmd_command_t *cmd_ptr;
00724 
00725     if (name == NULL || callback == NULL || strlen(name) == 0) {
00726         tr_warn("cmd_add invalid parameters");
00727         return;
00728     }
00729     cmd_ptr = (cmd_command_t *)MEM_ALLOC(sizeof(cmd_command_t));
00730     if (cmd_ptr == NULL) {
00731         tr_error("mem alloc failed in cmd_add");
00732         return;
00733     }
00734     cmd_ptr->name_ptr = name;
00735     cmd_ptr->info_ptr = info;
00736 #if MBED_CMDLINE_INCLUDE_MAN == 1
00737     cmd_ptr->man_ptr = man;
00738 #else
00739     cmd_ptr->man_ptr = 0;
00740 #endif
00741     cmd_ptr->run_cb = callback;
00742     cmd_ptr->busy = false;
00743     ns_list_add_to_end(&cmd.command_list, cmd_ptr);
00744     return;
00745 }
00746 
00747 void cmd_delete(const char *name)
00748 {
00749     cmd_command_t *cmd_ptr;
00750     cmd_ptr = cmd_find(name);
00751     if (cmd_ptr == NULL) {
00752         return;
00753     }
00754     ns_list_remove(&cmd.command_list, cmd_ptr);
00755     MEM_FREE(cmd_ptr);
00756     return;
00757 }
00758 static void replace_escapes(char *string_ptr)
00759 {
00760     while ((string_ptr = strchr(string_ptr, '\\')) != NULL) {
00761         memmove(string_ptr, string_ptr + 1, strlen(string_ptr + 1) + 1);
00762         string_ptr++;
00763     }
00764 }
00765 static int cmd_parse_argv(char *string_ptr, char **argv)
00766 {
00767     tr_deep("cmd_parse_argv(%s, ..)\r\n", string_ptr);
00768     int argc = 0;
00769     char *str_ptr, *end_quote_ptr = NULL;
00770 
00771     if (string_ptr == NULL || strlen(string_ptr) == 0) {
00772         tr_error("Invalid parameters");
00773         return 0;
00774     }
00775     str_ptr = string_ptr;
00776     do {
00777         argv[argc] = str_ptr;
00778         // tr_deep("parsing.. argv[%d]: %s\r\n", argc, str_ptr);
00779         if (*str_ptr != '\\') {
00780             if (*str_ptr == '"') {
00781                 // check if end quote
00782                 end_quote_ptr = str_ptr;
00783                 do {
00784                     end_quote_ptr = strchr(end_quote_ptr + 1, '\"');
00785                     if (end_quote_ptr == NULL) {
00786                         break;
00787                     }
00788                 } while (*(end_quote_ptr - 1) == '\\');
00789                 if (end_quote_ptr != NULL) {
00790                     // remove quotes give as one parameter
00791                     argv[argc]++;
00792                     str_ptr = end_quote_ptr;
00793                 } else {
00794                     str_ptr = strchr(str_ptr, ' ');
00795                 }
00796             } else {
00797                 str_ptr = strchr(str_ptr, ' ');
00798             }
00799         } else {
00800             str_ptr = strchr(str_ptr, ' ');
00801         }
00802         argc++; // one argument parsed
00803         if (str_ptr == NULL) {
00804             break;
00805         }
00806         if (argc > MAX_ARGUMENTS) {
00807             tr_warn("Maximum arguments (%d) reached", MAX_ARGUMENTS);
00808             break;
00809         }
00810         *str_ptr++ = 0;
00811         replace_escapes(argv[argc - 1]);
00812         // tr_deep("parsed argv[%d]: %s\r\n", argc-1, argv[argc-1]);
00813         while (*str_ptr == ' ') {
00814             str_ptr++;  // skip spaces
00815         };
00816     } while (*str_ptr != 0);
00817     return argc;
00818 }
00819 static void cmd_print_man(cmd_command_t *command_ptr)
00820 {
00821     if (command_ptr->man_ptr) {
00822         cmd_printf("%s\r\n", command_ptr->man_ptr);
00823     }
00824 }
00825 static void cmd_set_input(const char *str, int cur)
00826 {
00827     cmd_line_clear(cur);
00828     strcpy(cmd.input + cur, str);
00829     cmd.cursor = strlen(cmd.input);
00830 }
00831 /**
00832  * If oper is not null, function set null pointers
00833  */
00834 static char *next_command(char *string_ptr, operator_t *oper)
00835 {
00836     char *ptr = string_ptr;
00837     bool quote = false;
00838     while (*ptr != 0) {
00839         if (quote) {
00840             if (*ptr == '"') {
00841                 quote = false;
00842             }
00843         } else {
00844             //skip if previous character is '\'
00845             if ((ptr == string_ptr) || (*(ptr - 1) != '\\')) {
00846                 switch (*ptr) {
00847                     case ('"'): {
00848                         quote = true;
00849                         break;
00850                     }
00851                     case (';'):  //default operator
00852                         if (oper) {
00853                             *oper = OPERATOR_SEMI_COLON;
00854                             *ptr = 0;
00855                         }
00856                         return ptr + 1;
00857                     case ('&'):
00858                         if (ptr[1] == '&') {
00859                             if (oper) {
00860                                 *oper = OPERATOR_AND;
00861                                 *ptr = 0;
00862                             }
00863                             return ptr + 2;
00864                         } else {
00865                             if (oper) {
00866                                 *oper = OPERATOR_BACKGROUND;
00867                                 *ptr = 0;
00868                             }
00869                             return ptr + 1;
00870                         }
00871                     case ('|'):
00872                         if (ptr[1] == '|') {
00873                             if (oper) {
00874                                 *oper = OPERATOR_OR;
00875                                 *ptr = 0;
00876                             }
00877                             return ptr + 2;
00878                         } else {
00879                             tr_warn("pipe operator not supported");
00880                             if (oper) {
00881                                 *oper = OPERATOR_PIPE;
00882                                 *ptr = 0;
00883                             }
00884                             return ptr + 1;
00885                         }
00886                     default:
00887                         break;
00888                 }
00889             }
00890         }
00891         ptr++;
00892     }
00893     return 0;
00894 }
00895 static int cmd_run(char *string_ptr)
00896 {
00897     char *argv[MAX_ARGUMENTS];
00898     int argc, ret;
00899 
00900     tr_info("Executing cmd: '%s'", string_ptr);
00901     char *command_str = MEM_ALLOC(MAX_LINE);
00902     if (command_str == NULL) {
00903         tr_error("mem alloc failed in cmd_run");
00904         return CMDLINE_RETCODE_FAIL;
00905     }
00906     while (isspace((unsigned char) *string_ptr) &&
00907             *string_ptr != '\n' &&
00908             *string_ptr != 0) {
00909         string_ptr++; //skip white spaces
00910     }
00911     strcpy(command_str, string_ptr);
00912     tr_deep("cmd_run('%s') ", command_str);
00913     cmd_replace_alias(command_str);
00914     cmd_replace_variables(command_str);
00915     tr_debug("Parsed cmd: '%s'", command_str);
00916 
00917     argc = cmd_parse_argv(command_str, argv);
00918 
00919     cmd.cmd_ptr = cmd_find(argv[0]);
00920 
00921     if (cmd.cmd_ptr == NULL) {
00922         cmd_printf("Command '%s' not found.\r\n", argv[0]);
00923         MEM_FREE(command_str);
00924         return CMDLINE_RETCODE_COMMAND_NOT_FOUND;
00925     }
00926     if (cmd.cmd_ptr->run_cb == NULL) {
00927         tr_error("Command callback missing");
00928         MEM_FREE(command_str);
00929         return CMDLINE_RETCODE_COMMAND_CB_MISSING;
00930     }
00931 
00932     if (argc == 2 &&
00933             (cmd_has_option(argc, argv, "h") || cmd_parameter_index(argc, argv, "--help") > 0)) {
00934         MEM_FREE(command_str);
00935         cmd_print_man(cmd.cmd_ptr);
00936         return CMDLINE_RETCODE_SUCCESS;
00937     }
00938 
00939     if (cmd.cmd_ptr->busy) {
00940         MEM_FREE(command_str);
00941         return CMDLINE_RETCODE_COMMAND_BUSY;
00942     }
00943 
00944     // Run the actual callback
00945     cmd.cmd_ptr->busy = true;
00946     ret = cmd.cmd_ptr->run_cb(argc, argv);
00947     cmd_variable_add_int("?", ret);
00948     cmd_alias_add("_", string_ptr); // last executed command
00949     MEM_FREE(command_str);
00950     switch (ret) {
00951         case (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED):
00952             tr_warn("Command not implemented");
00953             break;
00954         case (CMDLINE_RETCODE_INVALID_PARAMETERS):
00955             tr_warn("Command parameter was incorrect");
00956             cmd_printf("Invalid parameters!\r\n");
00957             cmd_print_man(cmd.cmd_ptr);
00958             break;
00959         default:
00960             break;
00961     }
00962     return ret;
00963 }
00964 void cmd_escape_start(void)
00965 {
00966     cmd.escaping = true;
00967     memset(cmd.escape, 0, sizeof(cmd.escape));
00968     cmd.escape_index = 0;
00969 }
00970 static void cmd_arrow_right()
00971 {
00972     /* @todo handle shift
00973     if(strncmp(cmd.escape+1, "1;2", 3) == 0) {
00974         tr_debug("Shift pressed");
00975         shift = true;
00976     }
00977     */
00978     if ((cmd.escape_index == 1 && cmd.escape[0] == 'O') ||
00979             (cmd.escape_index == 4 && strncmp(cmd.escape + 1, "1;5", 3) == 0)) {
00980         cmd_move_cursor_to_next_space();
00981     } else {
00982         cmd.cursor ++;
00983     }
00984     if ((int)cmd.cursor > (int)strlen(cmd.input)) {
00985         cmd.cursor = strlen(cmd.input);
00986     }
00987 }
00988 static void cmd_arrow_left()
00989 {
00990     /* @todo handle shift
00991     if(strncmp(cmd.escape+1, "1;2", 3) == 0) {
00992         tr_debug("Shift pressed");
00993         shift = true;
00994     }
00995     */
00996     if ((cmd.escape_index == 1 && cmd.escape[0] == 'O') ||
00997             (cmd.escape_index == 4 && strncmp(cmd.escape + 1, "1;5", 3) == 0)) {
00998         cmd_move_cursor_to_last_space();
00999     } else {
01000         cmd.cursor --;
01001     }
01002     if (cmd.cursor < 0) {
01003         cmd.cursor = 0;
01004     }
01005 }
01006 static void cmd_arrow_up()
01007 {
01008     int16_t old_entry = cmd.history++;
01009     if (NULL == cmd_history_find(cmd.history)) {
01010         cmd.history = old_entry;
01011     }
01012     if (old_entry != cmd.history) {
01013         cmd_history_save(old_entry);
01014         cmd_history_get(cmd.history);
01015     }
01016 }
01017 static void cmd_arrow_down(void)
01018 {
01019     int16_t old_entry = cmd.history--;
01020     if (cmd.history < 0) {
01021         cmd.history = 0;
01022     }
01023 
01024     if (old_entry != cmd.history) {
01025         cmd_history_save(old_entry);
01026         cmd_history_get(cmd.history);
01027     }
01028 }
01029 void cmd_escape_read(int16_t u_data)
01030 {
01031     tr_debug("cmd_escape_read: %02x '%c' escape_index: %d: %s",
01032              u_data,
01033              (isprint(u_data) ? u_data : '?'),
01034              cmd.escape_index,
01035              cmd.escape);
01036 
01037     if (u_data == 'D') {
01038         cmd_arrow_left();
01039     } else if (u_data == 'C') {
01040         cmd_arrow_right();
01041     } else if (u_data == 'A') {
01042         cmd_arrow_up();
01043     } else if (u_data == 'B') {
01044         cmd_arrow_down();
01045     } else if (u_data == 'Z') {
01046         // Shift+TAB
01047         if (cmd.tab_lookup > 0) {
01048             cmd.cursor = cmd.tab_lookup;
01049             cmd.input[cmd.tab_lookup] = 0;
01050             if (cmd.tab_lookup_cmd_n > 0) {
01051                 cmd.tab_lookup_cmd_n--;
01052             }
01053         }
01054         cmd_tab_lookup();
01055     } else if (u_data == 'b') {
01056         cmd_move_cursor_to_last_space();
01057     } else if (u_data == 'f') {
01058         cmd_move_cursor_to_next_space();
01059     } else if (u_data == 'n') {
01060         if (cmd.escape[1] == '5') {
01061             // Device status report
01062             // Response: terminal is OK
01063             cmd_printf("%c0n", ESC);
01064         } else if (cmd.escape[1] == '6') {
01065             // Get cursor position
01066             cmd_printf(ESCAPE("%d%d"), 0, cmd.cursor);
01067         }
01068     } else if (u_data == 'R') {
01069         // response for Get cursor position (<esc>[6n)
01070         // <esc>[<lines>;<cols>R
01071         char *ptr;
01072         int lines = strtol(cmd.escape + 1, &ptr, 10);
01073         if (ptr == NULL) {
01074             tr_warn("Invalid response: <esc>%s%c", cmd.escape, u_data);
01075         } else {
01076             int cols = strtol(ptr + 1, 0, 10);
01077             tr_debug("Lines: %d, cols: %d", lines, cols);
01078             cmd_variable_add_int("LINES", lines);
01079             cmd_variable_add_int("COLUMNS", cols);
01080         }
01081     } else if (u_data == 'H') {
01082         // Xterm support
01083         cmd.cursor =  0;
01084     } else if (u_data == 'F') {
01085         // Xterm support
01086         cmd.cursor = strlen(cmd.input);
01087     } else if (isdigit((int)cmd.escape[cmd.escape_index - 1]) && u_data == '~') {
01088         switch (cmd.escape[cmd.escape_index - 1]) {
01089             case ('1'): //beginning-of-line     # Home key
01090                 cmd.cursor =  0;
01091                 break;
01092             case ('2'): //quoted-insert         # Insert key
01093                 cmd.insert = !cmd.insert;
01094                 break;
01095             case ('3'): //delete-char           # Delete key
01096                 if ((int)strlen(cmd.input) > (int)cmd.cursor) {
01097                     memmove(&cmd.input[cmd.cursor], &cmd.input[cmd.cursor + 1], strlen(&cmd.input[cmd.cursor + 1]) + 1);
01098                 }
01099                 break;
01100             case ('4'): //end-of-line           # End key
01101                 cmd.cursor = strlen(cmd.input);
01102                 break;
01103             case ('5'): //beginning-of-history  # PageUp key
01104                 goto_end_of_history();
01105                 break;
01106             case ('6'): //end-of-history        # PageDown key
01107                 goto_beginning_of_history();
01108                 break;
01109             default:
01110                 break;
01111         }
01112     } else if (isprint(u_data)) {  //IS_NUMBER || IS_CONTROL
01113         cmd.escape[cmd.escape_index++] = u_data;
01114         return;
01115     }
01116     cmd_output();
01117     cmd.escaping = false;
01118     return;
01119 }
01120 static void goto_end_of_history(void)
01121 {
01122     // handle new input if any and verify that
01123     // it is not already in beginning of history or current position
01124     bool allowStore = strlen(cmd.input) != 0; //avoid store empty lines to history
01125     cmd_history_t *entry_ptr;
01126     if (cmd.history > 0 && allowStore) {
01127         entry_ptr = cmd_history_find(cmd.history);
01128         if (entry_ptr) {
01129             if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) {
01130                 // current history contains contains same text as input
01131                 allowStore = false;
01132             }
01133         }
01134     } else if (allowStore && (entry_ptr = cmd_history_find(0)) != NULL) {
01135         if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) {
01136             //beginning of history was same text as input
01137             allowStore = false;
01138         }
01139     }
01140     if (allowStore) {
01141         cmd_history_save(0);  // new is saved to place 0
01142         cmd_history_save(-1); // new is created to the current one
01143     }
01144 
01145     cmd_history_t *cmd_ptr = ns_list_get_last(&cmd.history_list);
01146     cmd_set_input(cmd_ptr->command_ptr, 0);
01147     cmd.history =  ns_list_count(&cmd.history_list) - 1;
01148 }
01149 static void goto_beginning_of_history(void)
01150 {
01151     cmd_history_t *cmd_ptr = ns_list_get_first(&cmd.history_list);
01152     cmd_set_input(cmd_ptr->command_ptr, 0);
01153     cmd.history = 0;
01154 }
01155 static void cmd_reset_tab(void)
01156 {
01157     cmd.tab_lookup = 0;
01158     cmd.tab_lookup_cmd_n = 0;
01159     cmd.tab_lookup_n = 0;
01160 }
01161 void cmd_char_input(int16_t u_data)
01162 {
01163     if (cmd.prev_cr && u_data == '\n') {
01164         // ignore \n if previous character was \r ->
01165         // that triggers execute so \n does not need to anymore
01166         cmd.prev_cr = false; // unset to ensure we doesn't go here anymore
01167         return;
01168     }
01169     /*Handle passthrough*/
01170     if (cmd.passthrough_fnc != NULL) {
01171         cmd.passthrough_fnc(u_data);
01172         return;
01173     }
01174     /*handle ecape command*/
01175     if (cmd.escaping == true) {
01176         cmd_escape_read(u_data);
01177         return;
01178     }
01179 
01180     tr_debug("input char:      %02x '%c', cursor: %i, input: \"%s\"", u_data, (isprint(u_data) ? u_data : ' '), cmd.cursor,  cmd.input);
01181 
01182     /*Normal character input*/
01183     if (u_data == '\r' || u_data == '\n') {
01184         cmd.prev_cr = u_data == '\r';
01185         cmd_reset_tab();
01186         if (strlen(cmd.input) == 0) {
01187             if (cmd.echo) {
01188                 cmd_printf("\r\n");
01189                 cmd_output();
01190             }
01191         } else {
01192             if (cmd.echo) {
01193                 cmd_printf("\r\n");
01194             }
01195             cmd_execute();
01196         }
01197     } else if (u_data == ESC) {
01198         cmd_escape_start();
01199     } else if (u_data == BS || u_data == DEL) {
01200         cmd_reset_tab();
01201         cmd.cursor--;
01202         if (cmd.cursor < 0) {
01203             cmd.cursor = 0;
01204             return;
01205         }
01206         memmove(&cmd.input[cmd.cursor], &cmd.input[cmd.cursor + 1], strlen(&cmd.input[cmd.cursor + 1]) + 1);
01207         if (cmd.echo) {
01208             cmd_output();
01209         }
01210     } else if (u_data == ETX || u_data == CAN) {
01211         //ctrl+c (End of text) or ctrl+x (cancel)
01212         cmd_reset_tab();
01213         cmd_line_clear(0);
01214         if (!cmd.idle) {
01215             cmd_ready(CMDLINE_RETCODE_FAIL);
01216         }
01217         if (cmd.echo) {
01218             cmd_output();
01219         }
01220     } else if (u_data == ETB) {
01221         //ctrl+w (End of xmit block)
01222         tr_debug("ctrl+w - remove last word to cursor");
01223         cmd_clear_last_word();
01224         if (cmd.echo) {
01225             cmd_output();
01226         }
01227     } else if (u_data == TAB) {
01228         bool inc = false;
01229         if (cmd.tab_lookup > 0) {
01230             cmd.cursor = cmd.tab_lookup;
01231             cmd.input[cmd.tab_lookup] = 0;
01232             cmd.tab_lookup_cmd_n++;
01233             inc = true;
01234         } else {
01235             cmd.tab_lookup = strlen(cmd.input);
01236         }
01237 
01238         if (!cmd_tab_lookup()) {
01239             if (inc) {
01240                 cmd.tab_lookup_cmd_n--;
01241             }
01242             memset(cmd.input + cmd.tab_lookup, 0, MAX_LINE - cmd.tab_lookup);
01243         }
01244         if (cmd.echo) {
01245             cmd_output();
01246         }
01247 
01248     } else if (iscntrl(u_data)) {
01249         if (cmd.ctrl_fnc) {
01250             cmd.ctrl_fnc(u_data);
01251         }
01252     } else {
01253         cmd_reset_tab();
01254         tr_deep("cursor: %d, inputlen: %lu, u_data: %c\r\n", cmd.cursor, strlen(cmd.input), u_data);
01255         if ((strlen(cmd.input) >= MAX_LINE - 1) || (cmd.cursor >= MAX_LINE - 1)) {
01256             tr_warn("input buffer full");
01257             if (cmd.echo) {
01258                 cmd_output();
01259             }
01260             return;
01261         }
01262         if (cmd.insert) {
01263             memmove(&cmd.input[cmd.cursor + 1], &cmd.input[cmd.cursor], strlen(&cmd.input[cmd.cursor]) + 1);
01264         }
01265         cmd.input[cmd.cursor++] = u_data;
01266         if (cmd.echo) {
01267             cmd_output();
01268         }
01269     }
01270 }
01271 static int check_variable_keylookup_size(char **key, int *keysize)
01272 {
01273     if (cmd.cursor > 0 && cmd.tab_lookup > 0) {
01274         //printf("tab_lookup: %i\r\n", cmd.tab_lookup);
01275         char *ptr = cmd.input + cmd.tab_lookup;
01276         do {
01277             //printf("varkey lookup: %c\r\n", *ptr);
01278             if (*ptr == ' ') {
01279                 return 0;
01280             }
01281             if (*ptr == '$') {
01282                 int varlen = cmd.tab_lookup - (ptr - cmd.input) - 1;
01283                 *key = ptr;
01284                 *keysize = varlen;
01285                 //printf("varkey size: %i\r\n", varlen);
01286                 return (ptr - cmd.input);
01287             }
01288             ptr--;
01289         } while (ptr > cmd.input);
01290     }
01291     return 0;
01292 }
01293 bool cmd_tab_lookup(void)
01294 {
01295     int len = strlen(cmd.input);
01296     if (len == 0) {
01297         return false;
01298     }
01299     char *variable_keypart;
01300     int lookupSize;
01301     int varpos = check_variable_keylookup_size(&variable_keypart, &lookupSize);
01302 
01303     const char *str = NULL;
01304     if (varpos) {
01305         str = cmd_input_lookup_var(variable_keypart + 1, lookupSize, cmd.tab_lookup_cmd_n);
01306         if (str) {
01307             cmd_set_input(str, varpos + 1);
01308             return true;
01309         }
01310     } else {
01311         str = cmd_input_lookup(cmd.input, len, cmd.tab_lookup_cmd_n);
01312         if (str != NULL) {
01313             cmd_set_input(str, 0);
01314             return true;
01315         }
01316     }
01317     return false;
01318 }
01319 static void cmd_move_cursor_to_last_space(void)
01320 {
01321     if (cmd.cursor) {
01322         cmd.cursor--;
01323     } else {
01324         return;
01325     }
01326     const char *last_space = find_last_space(cmd.input + cmd.cursor, cmd.input);
01327     if (last_space) {
01328         cmd.cursor = last_space - cmd.input;
01329     } else {
01330         cmd.cursor = 0;
01331     }
01332 }
01333 static void cmd_move_cursor_to_next_space(void)
01334 {
01335     while (cmd.input[cmd.cursor] == ' ') {
01336         cmd.cursor++;
01337     }
01338     const char *next_space = strchr(cmd.input + cmd.cursor, ' ');
01339     if (next_space) {
01340         cmd.cursor = next_space - cmd.input;
01341     } else {
01342         cmd.cursor = (int)strlen(cmd.input);
01343     }
01344 }
01345 static void cmd_clear_last_word()
01346 {
01347     if (!cmd.cursor) {
01348         return;
01349     }
01350     char *ptr = cmd.input + cmd.cursor - 1;
01351     while (*ptr == ' ' && ptr >= cmd.input) {
01352         ptr--;
01353     }
01354     const char *last_space = find_last_space(ptr, cmd.input);
01355     if (last_space) {
01356         memmove((void *)last_space, &cmd.input[cmd.cursor], strlen(cmd.input + cmd.cursor) + 1);
01357         cmd.cursor = last_space - cmd.input;
01358     } else {
01359         memmove((void *)cmd.input, &cmd.input[cmd.cursor], strlen(cmd.input + cmd.cursor) + 1);
01360         cmd.cursor = 0;
01361     }
01362 }
01363 void cmd_output(void)
01364 {
01365     if (cmd.vt100_on && cmd.idle) {
01366         int curpos = (int)strlen(cmd.input) - cmd.cursor + 1;
01367         cmd_printf(CR_S CLEAR_ENTIRE_LINE "%s%s " MOVE_CURSOR_LEFT_N_CHAR,
01368                    cmdline_get_prompt(), cmd.input, curpos);
01369     }
01370 }
01371 void cmd_echo_off(void)
01372 {
01373     cmd_echo(false);
01374 }
01375 void cmd_echo_on(void)
01376 {
01377     cmd_echo(true);
01378 }
01379 // alias
01380 int replace_alias(char *str, const char *old_str, const char *new_str)
01381 {
01382     int old_len = strlen(old_str),
01383         new_len = strlen(new_str);
01384     if ((strncmp(str, old_str, old_len) == 0) &&
01385             ((str[ old_len ] == ' ') || (str[ old_len ] == 0) ||
01386              (str[ old_len ] == ';') || (str[ old_len ] == '&'))) {
01387         memmove(str + new_len, str + old_len, strlen(str + old_len) + 1);
01388         memcpy(str, new_str, new_len);
01389         return new_len - old_len;
01390     }
01391     return 0;
01392 }
01393 static void cmd_replace_alias(char *input)
01394 {
01395     ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) {
01396         replace_alias(input, cur_ptr->name_ptr, cur_ptr->value_ptr);
01397     }
01398 }
01399 //variable
01400 static void replace_variable(char *str, cmd_variable_t *variable_ptr)
01401 {
01402     const char *name = variable_ptr->name_ptr;
01403     int name_len = strlen(variable_ptr->name_ptr);
01404     char *value;
01405     char valueLocal[11];
01406     if (variable_ptr->type == VALUE_TYPE_STR) {
01407         value = variable_ptr->value.ptr;
01408     } else {
01409         value = valueLocal;
01410         snprintf(value, 11, "%d", variable_ptr->value.i);
01411     }
01412     char *tmp = MEM_ALLOC(name_len + 2);
01413     if (tmp == NULL) {
01414         tr_error("mem alloc failed in replace_variable");
01415         return;
01416     }
01417     tmp[0] = '$';
01418     strcpy(tmp + 1, name);
01419     replace_string(str, MAX_LINE, tmp, value);
01420     MEM_FREE(tmp);
01421 }
01422 static void cmd_replace_variables(char *input)
01423 {
01424     ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) {
01425         replace_variable(input, cur_ptr);
01426     }
01427 }
01428 //history
01429 static void cmd_history_item_delete(cmd_history_t *entry_ptr)
01430 {
01431     ns_list_remove(&cmd.history_list, entry_ptr);
01432     MEM_FREE(entry_ptr->command_ptr);
01433     MEM_FREE(entry_ptr);
01434 }
01435 
01436 static cmd_history_t *cmd_history_find(int16_t index)
01437 {
01438     cmd_history_t *entry_ptr = NULL;
01439     int16_t count = 0;
01440     ns_list_foreach(cmd_history_t, cur_ptr, &cmd.history_list) {
01441         if (count == index) {
01442             entry_ptr = cur_ptr;
01443             break;
01444         }
01445         count++;
01446     }
01447     return entry_ptr;
01448 }
01449 
01450 static void cmd_history_clean_overflow(void)
01451 {
01452     while (ns_list_count(&cmd.history_list) > cmd.history_max_count) {
01453         cmd_history_t *cmd_ptr = ns_list_get_last(&cmd.history_list);
01454         tr_debug("removing older history (%s)", cmd_ptr->command_ptr);
01455         cmd_history_item_delete(cmd_ptr);
01456     }
01457 }
01458 static void cmd_history_clean(void)
01459 {
01460     while (ns_list_count(&cmd.history_list) > 0) {
01461         tr_debug("removing older history");
01462         cmd_history_item_delete(ns_list_get_last(&cmd.history_list));
01463     }
01464 }
01465 static void cmd_history_save(int16_t index)
01466 {
01467     /*if entry true save it to first item which is the one currently edited*/
01468     cmd_history_t *entry_ptr;
01469     int16_t len;
01470 
01471     len = strlen(cmd.input);
01472 
01473     tr_debug("saving history item %d", index);
01474     entry_ptr = cmd_history_find(index);
01475 
01476     if (entry_ptr == NULL) {
01477         /*new entry*/
01478         entry_ptr = (cmd_history_t *)MEM_ALLOC(sizeof(cmd_history_t));
01479         if (entry_ptr == NULL) {
01480             tr_error("mem alloc failed in cmd_history_save");
01481             return;
01482         }
01483         entry_ptr->command_ptr = NULL;
01484         ns_list_add_to_start(&cmd.history_list, entry_ptr);
01485     }
01486 
01487     if (entry_ptr->command_ptr != NULL) {
01488         MEM_FREE(entry_ptr->command_ptr);
01489     }
01490     entry_ptr->command_ptr = (char *)MEM_ALLOC(len + 1);
01491     if (entry_ptr->command_ptr == NULL) {
01492         tr_error("mem alloc failed in cmd_history_save command_ptr");
01493         cmd_history_item_delete(entry_ptr);
01494         return;
01495     }
01496     strcpy(entry_ptr->command_ptr, cmd.input);
01497 
01498     cmd_history_clean_overflow();
01499 }
01500 static void cmd_history_get(uint16_t index)
01501 {
01502     cmd_history_t *entry_ptr;
01503 
01504     tr_debug("getting history item %d", index);
01505 
01506     entry_ptr = cmd_history_find(index);
01507 
01508     if (entry_ptr != NULL) {
01509         memset(cmd.input, 0, MAX_LINE);
01510         cmd_set_input(entry_ptr->command_ptr, 0);
01511     }
01512 }
01513 
01514 static void cmd_line_clear(int from)
01515 {
01516     memset(cmd.input + from, 0, MAX_LINE - from);
01517     cmd.cursor = from;
01518 }
01519 
01520 static void cmd_execute(void)
01521 {
01522     if (strlen(cmd.input) != 0) {
01523         bool noduplicates = true;
01524         cmd_history_t *entry_ptr = cmd_history_find(0);
01525         if (entry_ptr) {
01526             if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) {
01527                 noduplicates = false;
01528             }
01529         }
01530         if (noduplicates) {
01531             cmd_history_save(0);  // new is saved to place 0
01532             cmd_history_save(-1); // new is created to the current one
01533         }
01534     }
01535     cmd.history = 0;
01536 
01537     tr_deep("cmd_execute('%s') ", cmd.input);
01538     cmd_exe(cmd.input);
01539     cmd_line_clear(0);
01540 }
01541 
01542 
01543 static cmd_alias_t *alias_find(const char *alias)
01544 {
01545     cmd_alias_t *alias_ptr = NULL;
01546     if (alias == NULL || strlen(alias) == 0) {
01547         tr_error("alias_find invalid parameters");
01548         return NULL;
01549     }
01550 
01551     ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) {
01552         if (strcmp(alias, cur_ptr->name_ptr) == 0) {
01553             alias_ptr = cur_ptr;
01554             break;
01555         }
01556     }
01557     return alias_ptr;
01558 }
01559 
01560 static cmd_alias_t *alias_find_n(char *alias, int aliaslength, int n)
01561 {
01562     cmd_alias_t *alias_ptr = NULL;
01563     int i = 0;
01564     if (alias == NULL || strlen(alias) == 0) {
01565         tr_error("alias_find invalid parameters");
01566         return NULL;
01567     }
01568 
01569     ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) {
01570         if (strncmp(alias, cur_ptr->name_ptr, aliaslength) == 0) {
01571             if (i == n) {
01572                 alias_ptr = cur_ptr;
01573                 break;
01574             }
01575             i++;
01576         }
01577     }
01578     return alias_ptr;
01579 }
01580 static cmd_variable_t *variable_find(char *variable)
01581 {
01582     cmd_variable_t *variable_ptr = NULL;
01583     if (variable == NULL || strlen(variable) == 0) {
01584         tr_error("variable_find invalid parameters");
01585         return NULL;
01586     }
01587 
01588     ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) {
01589         if (strcmp(variable, cur_ptr->name_ptr) == 0) {
01590             variable_ptr = cur_ptr;
01591             break;
01592         }
01593     }
01594     return variable_ptr;
01595 }
01596 static cmd_variable_t *variable_find_n(char *variable, int length, int n)
01597 {
01598     cmd_variable_t *variable_ptr = NULL;
01599     if (variable == NULL || strlen(variable) == 0) {
01600         tr_error("variable_find invalid parameters");
01601         return NULL;
01602     }
01603     int i = 0;
01604     ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) {
01605         if (strncmp(variable, cur_ptr->name_ptr, length) == 0) {
01606             if (i == n) {
01607                 variable_ptr = cur_ptr;
01608                 break;
01609             }
01610             i++;
01611         }
01612     }
01613     return variable_ptr;
01614 }
01615 static void cmd_alias_print_all(void)
01616 {
01617     ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) {
01618         if (cur_ptr->name_ptr != NULL) {
01619             cmd_printf("%-18s'%s'\r\n", cur_ptr->name_ptr, cur_ptr->value_ptr ? cur_ptr->value_ptr : "");
01620         }
01621     }
01622     return;
01623 }
01624 static void cmd_variable_print_all(void)
01625 {
01626     ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) {
01627         if (cur_ptr->type == VALUE_TYPE_STR) {
01628             cmd_printf("%s='%s'\r\n", cur_ptr->name_ptr, cur_ptr->value.ptr ? cur_ptr->value.ptr : "");
01629         } else if (cur_ptr->type == VALUE_TYPE_INT) {
01630             cmd_printf("%s=%d\r\n", cur_ptr->name_ptr, cur_ptr->value.i);
01631         }
01632     }
01633     return;
01634 }
01635 
01636 void cmd_alias_add(const char *alias, const char *value)
01637 {
01638     cmd_alias_t *alias_ptr;
01639     if (alias == NULL || strlen(alias) == 0) {
01640         tr_warn("cmd_alias_add invalid parameters");
01641         return;
01642     }
01643     alias_ptr = alias_find(alias);
01644     if (alias_ptr == NULL) {
01645         if (value == NULL) {
01646             return;    // no need to add new null one
01647         }
01648         if (strlen(value) == 0) {
01649             return;    // no need to add new empty one
01650         }
01651         alias_ptr = (cmd_alias_t *)MEM_ALLOC(sizeof(cmd_alias_t));
01652         if (alias_ptr == NULL) {
01653             tr_error("Mem alloc fail in cmd_alias_add");
01654             return;
01655         }
01656         alias_ptr->name_ptr = (char *)MEM_ALLOC(strlen(alias) + 1);
01657         if (alias_ptr->name_ptr == NULL) {
01658             MEM_FREE(alias_ptr);
01659             tr_error("Mem alloc fail in cmd_alias_add name_ptr");
01660             return;
01661         }
01662         ns_list_add_to_end(&cmd.alias_list, alias_ptr);
01663         strcpy(alias_ptr->name_ptr, alias);
01664         alias_ptr->value_ptr = NULL;
01665     }
01666     if (value == NULL || strlen(value) == 0) {
01667         // delete this one
01668         ns_list_remove(&cmd.alias_list, alias_ptr);
01669         MEM_FREE(alias_ptr->name_ptr);
01670         MEM_FREE(alias_ptr->value_ptr);
01671         MEM_FREE(alias_ptr);
01672     } else {
01673         // add new or modify
01674         if (alias_ptr->value_ptr != NULL) {
01675             MEM_FREE(alias_ptr->value_ptr);
01676         }
01677         alias_ptr->value_ptr = (char *)MEM_ALLOC(strlen(value) + 1);
01678         if (alias_ptr->value_ptr == NULL) {
01679             cmd_alias_add(alias, NULL);
01680             tr_error("Mem alloc fail in cmd_alias_add value_ptr");
01681             return;
01682         }
01683         strcpy(alias_ptr->value_ptr, value);
01684     }
01685     return;
01686 }
01687 static cmd_variable_t *cmd_variable_add_prepare(char *variable, char *value)
01688 {
01689     if (variable == NULL || strlen(variable) == 0) {
01690         tr_warn("cmd_variable_add invalid parameters");
01691         return NULL;
01692     }
01693     cmd_variable_t *variable_ptr = variable_find(variable);
01694     if (variable_ptr == NULL) {
01695         if (value == NULL) {
01696             return NULL;    //  adding null variable
01697         }
01698         if (strlen(value) == 0) {
01699             return NULL;    // no need to add new empty one
01700         }
01701         variable_ptr = (cmd_variable_t *)MEM_ALLOC(sizeof(cmd_variable_t));
01702         if (variable_ptr == NULL) {
01703             tr_error("Mem alloc failed cmd_variable_add");
01704             return NULL;
01705         }
01706         variable_ptr->name_ptr = (char *)MEM_ALLOC(strlen(variable) + 1);
01707         if (variable_ptr->name_ptr == NULL) {
01708             MEM_FREE(variable_ptr);
01709             tr_error("Mem alloc failed cmd_variable_add name_ptr");
01710             return NULL;
01711         }
01712 
01713         ns_list_add_to_end(&cmd.variable_list, variable_ptr);
01714         strcpy(variable_ptr->name_ptr, variable);
01715         variable_ptr->value.ptr = NULL;
01716     }
01717     if (value == NULL || strlen(value) == 0) {
01718         // delete this one
01719         tr_debug("Remove variable: %s", variable);
01720         ns_list_remove(&cmd.variable_list, variable_ptr);
01721         MEM_FREE(variable_ptr->name_ptr);
01722         if (variable_ptr->type == VALUE_TYPE_STR) {
01723             MEM_FREE(variable_ptr->value.ptr);
01724         }
01725         MEM_FREE(variable_ptr);
01726         return NULL;
01727     }
01728     return variable_ptr;
01729 }
01730 void cmd_variable_add_int(char *variable, int value)
01731 {
01732     cmd_variable_t *variable_ptr = cmd_variable_add_prepare(variable, " ");
01733     if (variable_ptr == NULL) {
01734         return;
01735     }
01736     if (variable_ptr->value.ptr != NULL &&
01737             variable_ptr->type == VALUE_TYPE_STR) {
01738         // free memory
01739         MEM_FREE(variable_ptr->value.ptr);
01740     }
01741     variable_ptr->type = VALUE_TYPE_INT;
01742     variable_ptr->value.i = value;
01743 }
01744 void cmd_variable_add(char *variable, char *value)
01745 {
01746     cmd_variable_t *variable_ptr = cmd_variable_add_prepare(variable, value);
01747     if (variable_ptr == NULL) {
01748         return;
01749     }
01750     int value_len = strlen(value);
01751     replace_string(value, value_len, "\\n", "\n");
01752     replace_string(value, value_len, "\\r", "\r");
01753 
01754     // add new or modify
01755     int new_len = strlen(value) + 1;
01756     int old_len = 0;
01757     if (variable_ptr->value.ptr != NULL &&
01758             variable_ptr->type == VALUE_TYPE_STR) {
01759         // free memory if required
01760         old_len = strlen(variable_ptr->value.ptr) + 1;
01761         if (old_len != new_len) {
01762             MEM_FREE(variable_ptr->value.ptr);
01763         }
01764     }
01765     if (old_len != new_len) {
01766         variable_ptr->value.ptr = (char *)MEM_ALLOC(new_len);
01767         if (variable_ptr->value.ptr == NULL) {
01768             cmd_variable_add(variable, NULL);
01769             tr_error("Mem alloc failed cmd_variable_add value_ptr");
01770             return;
01771         }
01772     }
01773     variable_ptr->type = VALUE_TYPE_STR;
01774     strcpy(variable_ptr->value.ptr, value);
01775     return;
01776 }
01777 
01778 static bool is_cmdline_commands(char *command)
01779 {
01780     if ((strncmp(command, "alias", 5) == 0) ||
01781             (strcmp(command, "echo") == 0) ||
01782             (strcmp(command, "set") == 0) ||
01783             (strcmp(command, "clear") == 0) ||
01784             (strcmp(command, "help") == 0)) {
01785         return true;
01786     }
01787     return false;
01788 }
01789 /*Basic commands for cmd line
01790  * alias
01791  * echo
01792  * set
01793  * clear
01794  * help
01795  */
01796 int alias_command(int argc, char *argv[])
01797 {
01798     if (argc == 1) {
01799         // print all alias
01800         cmd_printf("alias:\r\n");
01801         cmd_alias_print_all();
01802     } else if (argc == 2) {
01803         // print alias
01804         if (is_cmdline_commands(argv[1])) {
01805             cmd_printf("Cannot overwrite default commands with alias\r\n");
01806             return -1;
01807         }
01808         tr_debug("Deleting alias %s", argv[1]);
01809         cmd_alias_add(argv[1], NULL);
01810     } else {
01811         // set alias
01812         tr_debug("Setting alias %s = %s", argv[1], argv[2]);
01813         cmd_alias_add(argv[1], argv[2]);
01814     }
01815     return 0;
01816 }
01817 int unset_command(int argc, char *argv[])
01818 {
01819     if (argc != 2) {
01820         return CMDLINE_RETCODE_INVALID_PARAMETERS;
01821     }
01822     tr_debug("Deleting variable %s", argv[1]);
01823     cmd_variable_add(argv[1], NULL);
01824     return 0;
01825 }
01826 int set_command(int argc, char *argv[])
01827 {
01828     if (argc == 1) {
01829         // print all alias
01830         cmd_printf("variables:\r\n");
01831         cmd_variable_print_all();
01832     } else if (argc == 2) {
01833         char *separator_ptr = strchr(argv[1], '=');
01834         if (!separator_ptr) {
01835             return CMDLINE_RETCODE_INVALID_PARAMETERS;
01836         }
01837         *separator_ptr = 0;
01838         cmd_variable_add(argv[1], separator_ptr + 1);
01839     } else {
01840         // set alias
01841         tr_debug("Setting variable %s = %s", argv[1], argv[2]);
01842         //handle special cases: vt100 on|off
01843         bool state;
01844         if (cmd_parameter_bool(argc, argv, "--vt100", &state)) {
01845             cmd.vt100_on = state;
01846             return 0;
01847         }
01848         if (cmd_parameter_bool(argc, argv, "--retcode", &state)) {
01849             cmd_variable_add(VAR_RETFMT, state ? DEFAULT_RETFMT : NULL);
01850             return 0;
01851         }
01852         char *str;
01853         if (cmd_parameter_val(argc, argv, "--retfmt", &str)) {
01854             cmd_variable_add(VAR_RETFMT, str);
01855             return 0;
01856         }
01857         cmd_variable_add(argv[1], argv[2]);
01858     }
01859     return 0;
01860 }
01861 int echo_command(int argc, char *argv[])
01862 {
01863     bool printEcho = false;
01864     if (argc == 1) {
01865         printEcho = true;
01866     } else if (argc == 2) {
01867         if (strcmp(argv[1], "off") == 0) {
01868             cmd_echo(false);
01869             printEcho = true;
01870         } else if (strcmp(argv[1], "on") == 0) {
01871             cmd_echo(true);
01872             printEcho = true;
01873         }
01874     }
01875     if (printEcho) {
01876         cmd_printf("ECHO is %s\r\n", cmd.echo ? "on" : "off");
01877     } else {
01878         for (int n = 1; n < argc; n++) {
01879             tr_deep("ECHO: %s\r\n", argv[n]);
01880             cmd_printf("%s ", argv[n]);
01881         }
01882         cmd_printf("\r\n");
01883     }
01884     return 0;
01885 }
01886 
01887 int clear_command(int argc, char *argv[])
01888 {
01889     (void)argc;
01890     (void)argv;
01891     cmd_echo(true);
01892     cmd_init_screen();
01893     return 0;
01894 }
01895 int help_command(int argc, char *argv[])
01896 {
01897     cmd_printf("Commands:\r\n");
01898     if (argc == 1) {
01899         ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) {
01900             cmd_printf("%-16s%s\r\n", cur_ptr->name_ptr, (cur_ptr->info_ptr ? cur_ptr->info_ptr : ""));
01901         }
01902     } else if (argc == 2) {
01903         cmd_command_t *cmd_ptr = cmd_find(argv[1]);
01904         if (cmd_ptr) {
01905             cmd_printf("Command: %s\r\n", cmd_ptr->name_ptr);
01906             if (cmd_ptr->man_ptr) {
01907                 cmd_printf("%s\r\n", cmd_ptr->man_ptr);
01908             } else if (cmd_ptr->info_ptr) {
01909                 cmd_printf("%s\r\n", cmd_ptr->info_ptr);
01910             }
01911         } else {
01912             cmd_printf("Command '%s' not found", argv[1]);
01913         }
01914     }
01915     return 0;
01916 }
01917 int true_command(int argc, char *argv[])
01918 {
01919     (void)argc;
01920     (void)argv;
01921     return CMDLINE_RETCODE_SUCCESS;
01922 }
01923 int false_command(int argc, char *argv[])
01924 {
01925     (void)argc;
01926     (void)argv;
01927     return CMDLINE_RETCODE_FAIL;
01928 }
01929 int history_command(int argc, char *argv[])
01930 {
01931     if (argc == 1) {
01932         int history_size = (int)ns_list_count(&cmd.history_list);
01933         cmd_printf("History [%i/%i]:\r\n", history_size - 1, cmd.history_max_count - 1);
01934         int i = 0;
01935         ns_list_foreach_reverse(cmd_history_t, cur_ptr, &cmd.history_list) {
01936             if (i != history_size - 1) {
01937                 cmd_printf("[%i]: %s\r\n", i++, cur_ptr->command_ptr);
01938             }
01939         }
01940     } else if (argc == 2) {
01941         if (strcmp(argv[1], "clear") == 0) {
01942             cmd_history_clean();
01943         } else {
01944             cmd_history_size(strtoul(argv[1], 0, 10));
01945         }
01946     }
01947     return 0;
01948 }
01949 
01950 /** Parameter helping functions
01951  */
01952 int cmd_parameter_index(int argc, char *argv[], const char *key)
01953 {
01954     int i = 0;
01955     for (i = 1; i < argc; i++) {
01956         if (strcmp(argv[i], key) == 0) {
01957             return i;
01958         }
01959     }
01960     return -1;
01961 }
01962 bool cmd_has_option(int argc, char *argv[], const char *key)
01963 {
01964     int i = 0;
01965     for (i = 1; i < argc; i++) {
01966         if (argv[i][0] == '-' &&  argv[i][1] != '-') {
01967             if (strstr(argv[i], key) != 0) {
01968                 return true;
01969             }
01970         }
01971     }
01972     return false;
01973 }
01974 bool cmd_parameter_bool(int argc, char *argv[], const char *key, bool *value)
01975 {
01976     int i = cmd_parameter_index(argc, argv, key);
01977     if (i > 0) {
01978         if (argc > (i + 1)) {
01979             if (strcmp(argv[i + 1], "on") == 0 ||
01980                     strcmp(argv[i + 1], "1") == 0 ||
01981                     strcmp(argv[i + 1], "true") == 0 ||
01982                     strcmp(argv[i + 1], "enable") == 0 ||
01983                     strcmp(argv[i + 1], "allow") == 0) {
01984                 *value = true;
01985             } else {
01986                 *value = false;
01987             }
01988             return true;
01989         }
01990     }
01991     return false;
01992 }
01993 bool cmd_parameter_val(int argc, char *argv[], const char *key, char **value)
01994 {
01995     int i = cmd_parameter_index(argc, argv, key);
01996     if (i > 0) {
01997         if (argc > (i + 1)) {
01998             *value = argv[i + 1];
01999             return true;
02000         }
02001     }
02002     return false;
02003 }
02004 bool cmd_parameter_int(int argc, char *argv[], const char *key, int32_t *value)
02005 {
02006     int i = cmd_parameter_index(argc, argv, key);
02007     char *tailptr;
02008     if (i > 0) {
02009         if (argc > (i + 1)) {
02010             *value = strtol(argv[i + 1], &tailptr, 10);
02011             if (0 == *tailptr) {
02012                 return true;
02013             }
02014             if (!isspace((unsigned char) *tailptr)) {
02015                 return false;
02016             } else {
02017                 return true;
02018             }
02019         }
02020     }
02021     return false;
02022 }
02023 bool cmd_parameter_float(int argc, char *argv[], const char *key, float *value)
02024 {
02025     int i = cmd_parameter_index(argc, argv, key);
02026     char *tailptr;
02027     if (i > 0) {
02028         if (argc > (i + 1)) {
02029             *value = strtof(argv[i + 1], &tailptr);
02030             if (0 == *tailptr) {
02031                 return true;    //Should be correct read always
02032             }
02033             if (!isspace((unsigned char) *tailptr)) {
02034                 return false;   //Garbage in tailptr
02035             } else {
02036                 return true;    //Spaces are fine after float
02037             }
02038         }
02039     }
02040     return false;
02041 }
02042 // convert hex string (eg. "76 ab ff") to binary array
02043 static int string_to_bytes(const char *str, uint8_t *buf, int bytes)
02044 {
02045     int len = strlen(str);
02046     if (len <= (3 * bytes - 1)) {
02047         int i;
02048         for (i = 0; i < bytes; i++) {
02049             if (i * 3 < len) {
02050                 buf[i] = (uint8_t)strtoul(str + i * 3, 0, 16);
02051             } else {
02052                 buf[i] = 0;
02053             }
02054         }
02055         return 0;
02056     }
02057     return -1;
02058 }
02059 
02060 static uint64_t read_64_bit(const uint8_t data_buf[__static 8])
02061 {
02062     uint64_t temp_64;
02063     temp_64 = (uint64_t)(*data_buf++) << 56;
02064     temp_64 += (uint64_t)(*data_buf++) << 48;
02065     temp_64 += (uint64_t)(*data_buf++) << 40;
02066     temp_64 += (uint64_t)(*data_buf++) << 32;
02067     temp_64 += (uint64_t)(*data_buf++) << 24;
02068     temp_64 += (uint64_t)(*data_buf++) << 16;
02069     temp_64 += (uint64_t)(*data_buf++) << 8;
02070     temp_64 += *data_buf++;
02071     return temp_64;
02072 }
02073 
02074 bool cmd_parameter_timestamp(int argc, char *argv[], const char *key, int64_t *value)
02075 {
02076     int i = cmd_parameter_index(argc, argv, key);
02077     if (i > 0) {
02078         if (argc > (i + 1)) {
02079             if (strchr(argv[i + 1], ',') != 0) {
02080                 // Format seconds,tics
02081                 const char splitValue[] = ", ";
02082                 char *token;
02083                 token = strtok(argv[i + 1], splitValue);
02084                 if (token) {
02085                     *value = (int64_t)strtoul(token, 0, 10) << 16;
02086                 }
02087                 token = strtok(NULL, splitValue);
02088                 if (token) {
02089                     *value |= (0xffff & strtoul(token, 0, 10));
02090                 }
02091             } else if (strchr(argv[i + 1], ':') != 0) {
02092                 // Format 00:00:00:00:00:00:00:00
02093                 uint8_t buf[8];
02094                 if (string_to_bytes(argv[i + 1], buf, 8) == 0) {
02095                     *value = read_64_bit(buf);
02096                 } else {
02097                     cmd_printf("timestamp should be 8 bytes long\r\n");
02098                 }
02099             } else {
02100                 // Format uint64
02101                 *value = strtol(argv[i + 1], 0, 10);
02102             }
02103             return true;
02104         }
02105     }
02106     return false;
02107 }
02108 char *cmd_parameter_last(int argc, char *argv[])
02109 {
02110     if (argc > 1) {
02111         return argv[ argc - 1 ];
02112     }
02113     return NULL;
02114 }
02115 /**
02116  * find last space but ignore nulls and first spaces.
02117  * used only internally to find out previous word
02118  * e.g.
02119  * const char str[] = "aaaab aa bbb\0\0";
02120  * const char* tmp = last_space(str+strlen(str), str);
02121  * printf(tmp); // prints "bbb"
02122  */
02123 static const char *find_last_space(const char *from, const char *to)
02124 {
02125     if (from <= to) {
02126         return 0;
02127     }
02128     while ((from > to) && ((*from == 0) || (*from == ' '))) {
02129         from--;
02130     }
02131     while (from > to) {
02132         if (*from == ' ') {
02133             return from + 1;
02134         }
02135         from--;
02136     }
02137     return 0;
02138 }
02139 static int replace_string(
02140     char *str, int str_len,
02141     const char *old_str, const char *new_str)
02142 {
02143     char *ptr = str;
02144     char *end = str + str_len;
02145     int old_len = strlen(old_str);
02146     int new_len = strlen(new_str);
02147     if (old_len > 0) {
02148         tr_deep("find: '%s'\r\n", old_str);
02149         while ((ptr = strstr(ptr, old_str)) != 0) {
02150             if (ptr + new_len > end) {
02151                 tr_warn("Buffer was not enough for replacing\r\n");
02152                 return 1;
02153             }
02154             tr_warn("old_str found\r\n");
02155             if (old_len != new_len) {
02156                 tr_warn("memmove\r\n");
02157                 memmove(ptr + new_len, ptr + old_len, strlen(ptr + old_len) + 1);
02158             }
02159             memcpy(ptr, new_str, new_len);
02160             ptr += new_len;
02161         }
02162     }
02163     return 0;
02164 }