takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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(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 }