| /* |
| <:copyright-BRCM:2016:DUAL/GPL:standard |
| |
| Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| All Rights Reserved |
| |
| Unless you and Broadcom execute a separate written software license |
| agreement governing use of this software, this software is licensed |
| to you under the terms of the GNU General Public License version 2 |
| (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| with the following added to such license: |
| |
| As a special exception, the copyright holders of this software give |
| you permission to link this software with independent modules, and |
| to copy and distribute the resulting executable under terms of your |
| choice, provided that you also meet, for each linked independent |
| module, the terms and conditions of the license of that module. |
| An independent module is a module which is not derived from this |
| software. The special exception does not apply to any modifications |
| of the software. |
| |
| Not withstanding the above, under no circumstances may you combine |
| this software in any way with any other Broadcom software provided |
| under a license other than the GPL, without Broadcom's express prior |
| written consent. |
| |
| :> |
| */ |
| |
| |
| /******************************************************************* |
| * bcmcli.c |
| * |
| * CLI engine |
| * |
| *******************************************************************/ |
| #include <bcmos_system.h> |
| #define BCMCLI_INTERNAL |
| #include <bcmcli.h> |
| #include <bcmos_types.h> |
| |
| #define BCMCLI_INBUF_LEN 2048 |
| #define BCMCLI_MAX_QUAL_NAME_LENGTH 256 |
| #define BCMCLI_MAX_PARMS 128 |
| #define BCMCLI_UP_STR ".." |
| #define BCMCLI_ROOT_STR "/" |
| #define BCMCLI_COMMENT_CHAR '#' |
| #define BCMCLI_HELP_CHAR '?' |
| #define BCMCLI_ARRAY_DELIM_CHAR ',' |
| #define BCMCLI_ROOT_HELP "root directory" |
| #define BCMCLI_MAX_PARM_VAL_LEN 256 |
| #define BCMCLI_ENUM_MASK_DEL_CHAR '+' |
| #define BCMCLI_HELP_BUFFER_SIZE 16384 |
| |
| #define BCMCLI_EQUAL_CHAR '=' |
| |
| |
| typedef enum { BCMCLI_ENTRY_DIR, BCMCLI_ENTRY_CMD } bcmcli_entry_selector; |
| |
| /* External table - boolean values */ |
| bcmcli_enum_val bcmcli_enum_bool_table[] = { |
| { .name="true", .val = 1 }, |
| { .name="yes", .val = 1 }, |
| { .name="on", .val = 1 }, |
| { .name="false", .val = 0 }, |
| { .name="no", .val = 0 }, |
| { .name="off", .val = 0 }, |
| BCMCLI_ENUM_LAST |
| }; |
| |
| /* Monitor token structure */ |
| struct bcmcli_entry |
| { |
| struct bcmcli_entry *next; |
| char *name; /* Command/directory name */ |
| char *help; /* Command/directory help */ |
| bcmcli_entry_selector sel; /* Entry selector */ |
| char *alias; /* Alias */ |
| uint16_t alias_len; /* Alias length */ |
| struct bcmcli_entry *parent; /* Parent directory */ |
| bcmcli_access_right access_right; |
| |
| union { |
| struct |
| { |
| struct bcmcli_entry *first; /* First entry in directory */ |
| bcmcli_dir_extra_parm extras; /* Optional extras */ |
| } dir; |
| struct |
| { |
| bcmcli_cmd_cb cmd_cb; /* Command callback */ |
| bcmcli_cmd_parm *parms; /* Command parameters */ |
| bcmcli_cmd_extra_parm extras; /* Optional extras */ |
| uint16_t num_parms; |
| } cmd; |
| } u; |
| }; |
| |
| |
| /* Token types */ |
| typedef enum |
| { |
| BCMCLI_TOKEN_EMPTY, |
| BCMCLI_TOKEN_UP, |
| BCMCLI_TOKEN_ROOT, |
| BCMCLI_TOKEN_BREAK, |
| BCMCLI_TOKEN_HELP, |
| BCMCLI_TOKEN_NAME, |
| BCMCLI_TOKEN_VALUE, |
| } bcmcli_token_type; |
| |
| /* Parameter value set descriptor */ |
| typedef union bcmcli_parm_value_status |
| { |
| bcmos_bool value_set; |
| bcmos_bool *values_set; |
| } bcmcli_parm_value_status; |
| |
| /* CLI session data */ |
| typedef struct bcmcli_session_data |
| { |
| bcmcli_entry *curdir; |
| bcmcli_entry *curcmd; |
| bcmcli_cmd_parm cmd_parms[BCMCLI_MAX_PARMS]; |
| bcmcli_parm_value_status value_status[BCMCLI_MAX_PARMS]; |
| bcmcli_session *session; |
| uint16_t num_parms; |
| char *p_inbuf; |
| int stop_monitor; |
| char inbuf[BCMCLI_INBUF_LEN]; |
| } bcmcli_session_extras; |
| |
| /* Name, value pairs */ |
| typedef struct bcmcli_name_value |
| { |
| bcmcli_token_type type; |
| const char *name; |
| const char *value; |
| } bcmcli_name_value; |
| |
| static bcmcli_entry *_bcmcli_root_dir; |
| static bcmcli_session_extras *_bcmcli_root_session; |
| static bcmcli_log_mode _bcmcli_log_mode; |
| static bcmcli_session *_bcmcli_log_session; |
| |
| #define BCMCLI_MIN_NAME_LENGTH_FOR_ALIAS 3 |
| #define BCMCLI_ROOT_NAME "/" |
| |
| /* Internal functions */ |
| static void _bcmcli_alloc_root(const bcmcli_session_parm *parm); |
| static void _bcmcli_display_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir ); |
| static bcmcli_token_type _bcmcli_get_word(bcmcli_session_extras *session, char **inbuf, char **p_word); |
| static bcmcli_token_type _bcmcli_analyze_token( const char *name ); |
| static int _bcmcli_parse_parms( bcmcli_session_extras *mon_session, bcmcli_entry *p_token, |
| bcmcli_name_value *pairs, int npairs); |
| static int _bcmcli_extend_parms( bcmcli_session_extras *mon_session, bcmcli_name_value *pairs, |
| int npairs, bcmos_bool last_is_space, char *insert_str, uint32_t insert_size); |
| static bcmcli_entry *_bcmcli_search_token( bcmcli_entry *p_dir, const char *name ); |
| static void _bcmcli_help_dir( bcmcli_session_extras *mon_session, bcmcli_entry *p_dir ); |
| static void _bcmcli_help_entry(bcmcli_session_extras *mon_session, bcmcli_entry *p_token, |
| bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print); |
| static void _bcmcli_help_populated_cmd(bcmcli_session_extras *mon_session, bcmcli_entry *p_token, |
| const char *partial_match, bcmos_bool suppress_assigned); |
| static void _bcmcli_choose_alias( bcmcli_entry *p_dir, bcmcli_entry *p_new_token ); |
| static bcmcli_cmd_parm *_bcmcli_find_named_parm(bcmcli_session_extras *mon_session, const char *name); |
| static char *_bcmcli_strlwr( char *s ); |
| static int _bcmcli_stricmp( const char *s1, const char *s2, int len ); |
| static bcmos_errno _bcmcli_dft_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val); |
| static const char *_bcmcli_get_type_name(const bcmcli_cmd_parm *parm); |
| static void _bcmcli_dft_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size); |
| static bcmos_errno _bcmcli_enum_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val); |
| static void _bcmcli_enum_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size); |
| static bcmos_errno _bcmcli_enum_mask_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val); |
| static void _bcmcli_enum_mask_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size); |
| static bcmos_errno _bcmcli_buffer_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val); |
| static const char *_bcmcli_qualified_name( bcmcli_entry *token, char *buffer, int size); |
| static bcmos_errno _bcmcli_split(bcmcli_session_extras *mon_session, bcmcli_name_value **pairs, int *npairs); |
| static void _bcmcli_assign_callbacks(bcmcli_cmd_parm *parm); |
| static void _bcmcli_log_cmd(const char *cmd); |
| static void _bcmcli_log_rc(bcmos_errno rc); |
| static void _bcmcli_free_session_value_status(bcmcli_session_extras *mon_session); |
| |
| static inline bcmcli_session_extras *_bcmcli_session_data(bcmcli_session *session) |
| { |
| if (!session) |
| return _bcmcli_root_session; |
| return bcmcli_session_data(session); |
| } |
| |
| /** Add subdirectory to the parent directory |
| * |
| * \param[in] parent Parent directory handle. NULL=root |
| * \param[in] name Directory name |
| * \param[in] help Help string |
| * \param[in] access_right Access rights |
| * \param[in] extras Optional directory descriptor. Mustn't be allocated on the stack. |
| * \return new directory handle or NULL in case of failure |
| */ |
| bcmcli_entry *bcmcli_dir_add(bcmcli_entry *parent, const char *name, |
| const char *help, bcmcli_access_right access_right, |
| const bcmcli_dir_extra_parm *extras) |
| { |
| bcmcli_entry *p_dir; |
| bcmcli_entry **p_e; |
| |
| assert(name); |
| assert(help); |
| if (!name || !help) |
| return NULL; |
| |
| if (!_bcmcli_root_dir) |
| { |
| _bcmcli_alloc_root(NULL); |
| if (!_bcmcli_root_dir) |
| return NULL; |
| } |
| |
| if (!parent) |
| parent = _bcmcli_root_dir; |
| |
| p_dir=(bcmcli_entry *)bcmos_calloc( sizeof(bcmcli_entry) + strlen(name) + strlen(help) + 2 ); |
| if ( !p_dir ) |
| return NULL; |
| |
| p_dir->name = (char *)(p_dir + 1); |
| strcpy( p_dir->name, name); |
| p_dir->help = p_dir->name + strlen(name) + 1; |
| strcpy(p_dir->help, help); |
| p_dir->sel = BCMCLI_ENTRY_DIR; |
| _bcmcli_choose_alias( parent, p_dir ); |
| p_dir->access_right = access_right; |
| if (extras) |
| p_dir->u.dir.extras = *extras; |
| |
| /* Add new directory to the parent's list */ |
| p_dir->parent = parent; |
| p_e = &(parent->u.dir.first); |
| while (*p_e) |
| p_e = &((*p_e)->next); |
| *p_e = p_dir; |
| |
| return p_dir; |
| } |
| |
| static bcmcli_entry * find_entry_in_dir( bcmcli_entry *dir, const char *name, |
| bcmcli_entry_selector type, uint16_t recursive_search) |
| { |
| bcmcli_entry *p1, *p; |
| |
| if ( !dir ) |
| { |
| dir = _bcmcli_root_dir; |
| if (!dir) |
| return NULL; |
| } |
| p = dir->u.dir.first; |
| while (p) |
| { |
| if ( !_bcmcli_stricmp(p->name, name, -1) && type == p->sel ) |
| return p; |
| if ( recursive_search && p->sel == BCMCLI_ENTRY_DIR ) |
| { |
| p1 = find_entry_in_dir(p, name , type, 1 ); |
| if ( p1 ) |
| return p1; |
| } |
| p = p->next; |
| } |
| return NULL; |
| } |
| |
| |
| /* Scan directory tree and look for directory with name starts from |
| * root directory with name root_name |
| */ |
| bcmcli_entry *bcmcli_dir_find(bcmcli_entry *parent, const char *name) |
| { |
| if ( !parent ) |
| parent = _bcmcli_root_dir; |
| return find_entry_in_dir(parent, name, BCMCLI_ENTRY_DIR, 0 ); |
| } |
| |
| |
| /* Scan directory tree and look for command named "name". */ |
| bcmcli_entry *bcmcli_cmd_find(bcmcli_entry *parent, const char *name ) |
| { |
| if ( !parent ) |
| parent = _bcmcli_root_dir; |
| return find_entry_in_dir(parent, name, BCMCLI_ENTRY_CMD, 0 ); |
| } |
| |
| |
| /** Add CLI command |
| * |
| * \param[in] dir Handle of directory to add command to. NULL=root |
| * \param[in] name Command name |
| * \param[in] cmd_cb Command handler |
| * \param[in] help Help string |
| * \param[in] access_right Access rights |
| * \param[in] extras Optional extras |
| * \param[in] parms Optional parameters array. Must not be allocated on the stack! |
| * If parms!=NULL, the last parameter in the array must have name==NULL. |
| * \return |
| * 0 =OK\n |
| * <0 =error code |
| */ |
| bcmos_errno bcmcli_cmd_add(bcmcli_entry *dir, const char *name, bcmcli_cmd_cb cmd_cb, |
| const char *help, bcmcli_access_right access_right, |
| const bcmcli_cmd_extra_parm *extras, bcmcli_cmd_parm parms[]) |
| { |
| bcmcli_entry *p_token; |
| bcmcli_entry **p_e; |
| uint16_t i; |
| bcmcli_cmd_parm *parm = parms; |
| |
| assert(name); |
| assert(help); |
| assert(cmd_cb); |
| if (!name || !cmd_cb || !help) |
| return BCM_ERR_PARM; |
| |
| if (!_bcmcli_root_dir) |
| { |
| _bcmcli_alloc_root(NULL); |
| if (!_bcmcli_root_dir) |
| return BCM_ERR_NOMEM; |
| } |
| |
| if (!dir) |
| dir = _bcmcli_root_dir; |
| |
| p_token=(bcmcli_entry *)bcmos_calloc( sizeof(bcmcli_entry) + strlen(name) + strlen(help) + 2 ); |
| if ( !p_token ) |
| return BCM_ERR_NOMEM; |
| |
| /* Copy name */ |
| p_token->name = (char *)(p_token + 1); |
| strcpy( p_token->name, name ); |
| p_token->help = p_token->name + strlen(name) + 1; |
| strcpy(p_token->help, help); |
| p_token->sel = BCMCLI_ENTRY_CMD; |
| p_token->u.cmd.cmd_cb = cmd_cb; |
| p_token->u.cmd.parms = parms; |
| if (extras) |
| p_token->u.cmd.extras = *extras; |
| p_token->access_right = access_right; |
| |
| /* Convert name to lower case and choose alias */ |
| _bcmcli_choose_alias(dir, p_token ); |
| |
| |
| /* Check parameters */ |
| for (i = 0; i < BCMCLI_MAX_PARMS && parms && parms[i].name; i++) |
| { |
| parm = &parms[i]; |
| /* User-defined parameter must have a scan_cb callback for text->value conversion */ |
| if ((parm->type==BCMCLI_PARM_USERDEF) && !parm->scan_cb) |
| { |
| bcmos_printf("MON: %s> scan_cb callback must be set for user-defined parameter %s\n", name, parm->name); |
| goto cmd_add_error; |
| } |
| if (parm->type==BCMCLI_PARM_ENUM || parm->type==BCMCLI_PARM_ENUM_MASK) |
| { |
| if (!parm->enum_table) |
| { |
| bcmos_printf("MON: %s> value table must be set in low_val for enum parameter %s\n", name, parm->name); |
| goto cmd_add_error; |
| } |
| |
| /* Check default value if any */ |
| if ((parm->flags & BCMCLI_PARM_FLAG_DEFVAL)) |
| { |
| if (_bcmcli_enum_mask_scan_cb(parm, &parm->value, parm->value.string) < 0) |
| { |
| bcmos_printf("MON: %s> default value %s doesn't match any value of enum parameter %s\n", name, parm->value.string, parm->name); |
| goto cmd_add_error; |
| } |
| } |
| else if ((parm->flags & BCMCLI_PARM_FLAG_OPTIONAL)) |
| { |
| /* Optional enum parameters are initialized by their 1st value by default. |
| * All other parameters are initialized to 0. |
| */ |
| bcmcli_enum_val *values=parm->enum_table; |
| parm->value.enum_val = values[0].val; |
| } |
| |
| /* All values of enum mask parameters mast be complementary bits */ |
| if (parm->type==BCMCLI_PARM_ENUM_MASK) |
| { |
| long all_mask = 0; |
| bcmcli_enum_val *values; |
| for (values=parm->enum_table; values->name; ++values) |
| all_mask |= values->val; |
| |
| for (values=parm->enum_table; values->name; ++values) |
| { |
| if ((all_mask & values->val) != values->val) |
| { |
| bcmos_printf("MON: %s> enum_table values of enum_mask parameters must be complementary bits\n", name, parm->name); |
| goto cmd_add_error; |
| } |
| all_mask &= ~values->val; |
| } |
| } |
| } |
| else if (parm->type==BCMCLI_PARM_BUFFER) |
| { |
| if (!parm->value.buffer.start || !parm->value.buffer.len) |
| { |
| bcmos_printf("MON: %s> value.buffer.start is not set for BUFFER parameter %s\n", name, parm->name); |
| goto cmd_add_error; |
| } |
| if (parm->max_array_size) |
| { |
| bcmos_printf("MON: %s> BUFFER arrays are not supported %s\n", name, parm->name); |
| goto cmd_add_error; |
| } |
| } |
| if (parm->max_array_size) |
| { |
| if (!parm->values) |
| { |
| bcmos_printf("MON: %s> parm->values must be set for parameter-array %s\n", name, parm->name); |
| goto cmd_add_error; |
| } |
| } |
| _bcmcli_assign_callbacks(parm); |
| } |
| if ((i == BCMCLI_MAX_PARMS) && parms[i].name[0]) |
| { |
| bcmos_printf("MON: %s> too many parameters\n", name); |
| goto cmd_add_error; |
| } |
| p_token->u.cmd.num_parms = i; |
| |
| /* Add token to the directory */ |
| p_token->parent = dir; |
| p_e = &(dir->u.dir.first); |
| while (*p_e) |
| p_e = &((*p_e)->next); |
| *p_e = p_token; |
| |
| return 0; |
| |
| cmd_add_error: |
| bcmos_free( p_token ); |
| return BCM_ERR_PARM; |
| } |
| |
| |
| /** Destroy token (command or directory) |
| * \param[in] token Directory or command token. NULL=root |
| */ |
| void bcmcli_token_destroy(bcmcli_entry *token) |
| { |
| if (!token) |
| { |
| if (!_bcmcli_root_dir) |
| return; |
| token = _bcmcli_root_dir; |
| } |
| /* Remove from parent's list */ |
| if (token->parent) |
| { |
| bcmcli_entry **p_e; |
| p_e = &(token->parent->u.dir.first); |
| while (*p_e) |
| { |
| if (*p_e == token) |
| { |
| *p_e = token->next; |
| break; |
| } |
| p_e = &((*p_e)->next); |
| } |
| } |
| |
| /* Remove all directory entries */ |
| if (token->sel == BCMCLI_ENTRY_DIR) |
| { |
| bcmcli_entry *e = token->u.dir.first; |
| while ((e = token->u.dir.first)) |
| bcmcli_token_destroy(e); |
| } |
| else if (token->u.cmd.extras.free_parms) |
| token->u.cmd.extras.free_parms(token->u.cmd.parms); |
| |
| /* Release the token */ |
| bcmos_free(token); |
| |
| if (token == _bcmcli_root_dir) |
| { |
| _bcmcli_root_dir = NULL; |
| if (_bcmcli_root_session) |
| { |
| bcmcli_session_close(_bcmcli_root_session->session); |
| _bcmcli_root_session = NULL; |
| } |
| } |
| } |
| |
| /** Open monitor session |
| * |
| * Monitor supports multiple simultaneous sessions with different |
| * access rights. |
| * Note that there already is a default session with full administrative rights, |
| * that takes input from stdin and outputs to stdout. |
| * \param[in] parm Session parameters. Must not be allocated on the stack. |
| * \param[out] p_session Session handle |
| * \return |
| * 0 =OK\n |
| * <0 =error code |
| */ |
| bcmos_errno bcmcli_session_open(const bcmcli_session_parm *parm, bcmcli_session **p_session) |
| { |
| bcmcli_session *session; |
| bcmcli_session_extras *mon_session; |
| bcmcli_session_parm session_parms; |
| int rc; |
| |
| assert(p_session); |
| if (!p_session) |
| return BCM_ERR_PARM; |
| |
| if (!_bcmcli_root_dir) |
| { |
| _bcmcli_alloc_root(parm); |
| if (!_bcmcli_root_dir) |
| return BCM_ERR_NOMEM; |
| } |
| if (parm) |
| session_parms = *parm; |
| else |
| { |
| memset(&session_parms, 0, sizeof(session_parms)); |
| session_parms.name = "unnamed"; |
| } |
| |
| /* Open comm session */ |
| session_parms.extra_size = sizeof(bcmcli_session_extras); |
| rc = bcmcli_session_open_user(&session_parms, &session); |
| if (rc) |
| return rc; |
| mon_session = _bcmcli_session_data(session); |
| mon_session->curdir = _bcmcli_root_dir; |
| mon_session->session = session; |
| |
| *p_session = session; |
| |
| return 0; |
| } |
| |
| #define BCMCLI_PARSE_RETURN(ret) \ |
| do { \ |
| rc = ret; \ |
| goto bcmcli_parse_out; \ |
| } while (0) |
| |
| /* Parse a single command. Stop on ';' or EOL */ |
| static bcmos_errno bcmcli_parse_command(bcmcli_session *session) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| bcmcli_entry *p_token; |
| bcmcli_name_value *pairs = NULL; |
| int stop_parsing = 0; |
| int npairs; |
| int i; |
| char *cmd_line; |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| session = mon_session->session; |
| |
| /* Make a copy of command line - for logging */ |
| cmd_line = bcmos_alloc(strlen(mon_session->p_inbuf) + 1); |
| if (!cmd_line) |
| return BCM_ERR_NOMEM; |
| strcpy(cmd_line, mon_session->p_inbuf); |
| |
| /* Split string to name/value pairs */ |
| rc = _bcmcli_split(mon_session, &pairs, &npairs); |
| if (rc) |
| { |
| if (rc == BCM_ERR_NOENT) |
| rc = BCM_ERR_OK; |
| BCMCLI_PARSE_RETURN(rc); |
| } |
| |
| /* Interpret empty string as "display directory" */ |
| if ( !npairs ) |
| { |
| _bcmcli_display_dir(mon_session, mon_session->curdir ); |
| BCMCLI_PARSE_RETURN(BCM_ERR_OK); |
| } |
| |
| /* Identify parameters */ |
| for (i=0; i<npairs && !rc && !stop_parsing; i++) |
| { |
| switch (pairs[i].type) |
| { |
| case BCMCLI_TOKEN_NAME: |
| case BCMCLI_TOKEN_VALUE: |
| /* Identify command. The 1st pair can't contain name, only value */ |
| if (pairs[i].name) |
| { |
| bcmcli_session_print(session, "**ERR: %s is unexpected\n", pairs[i].name); |
| BCMCLI_PARSE_RETURN(BCM_ERR_PARM); |
| } |
| p_token = _bcmcli_search_token(mon_session->curdir, pairs[i].value); |
| if (p_token == NULL) |
| { |
| bcmcli_session_print(session, "**ERR: %s is unexpected\n", pairs[i].value); |
| BCMCLI_PARSE_RETURN(BCM_ERR_PARM); |
| } |
| /* Directory or command ? */ |
| if (p_token->sel == BCMCLI_ENTRY_DIR) |
| { |
| mon_session->curdir = p_token; |
| _bcmcli_display_dir(mon_session, mon_session->curdir ); |
| } |
| else |
| { |
| /* Function token */ |
| mon_session->curcmd = p_token; |
| if (_bcmcli_parse_parms(mon_session, p_token, &pairs[i+1], npairs-i-1) < 0) |
| { |
| _bcmcli_help_entry(mon_session, p_token, &pairs[i+1], npairs-i-1, BCMOS_TRUE); |
| rc = BCM_ERR_PARM; |
| } |
| else |
| { |
| _bcmcli_log_cmd(cmd_line); |
| rc = p_token->u.cmd.cmd_cb(session, mon_session->cmd_parms, npairs-i-1 ); |
| if (rc) |
| { |
| char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH]; |
| bcmcli_session_print(session, "MON: %s> failed with error code %s(%d)\n", |
| _bcmcli_qualified_name(p_token, buffer, sizeof(buffer)), |
| bcmos_strerror(rc), rc); |
| } |
| _bcmcli_log_rc(rc); |
| _bcmcli_free_session_value_status(mon_session); |
| } |
| stop_parsing = 1; |
| } |
| break; |
| |
| case BCMCLI_TOKEN_UP: /* Go to upper directory */ |
| if (mon_session->curdir->parent) |
| mon_session->curdir = mon_session->curdir->parent; |
| _bcmcli_display_dir(mon_session, mon_session->curdir ); |
| break; |
| |
| case BCMCLI_TOKEN_ROOT: /* Go to the root directory */ |
| mon_session->curdir = _bcmcli_root_dir; |
| _bcmcli_display_dir(mon_session, mon_session->curdir ); |
| break; |
| |
| case BCMCLI_TOKEN_HELP: /* Display help */ |
| if (i < npairs-1 && |
| ((p_token = _bcmcli_search_token( mon_session->curdir, pairs[i+1].value)) != NULL )) |
| { |
| _bcmcli_help_entry(mon_session, p_token, &pairs[i+2], npairs-i-2, BCMOS_FALSE); |
| } |
| else |
| { |
| _bcmcli_help_dir(mon_session, mon_session->curdir); |
| } |
| stop_parsing = 1; |
| break; |
| |
| default: |
| stop_parsing = 1; |
| break; |
| } |
| } |
| |
| bcmcli_parse_out: |
| if (pairs) |
| bcmos_free(pairs); |
| if (cmd_line) |
| bcmos_free(cmd_line); |
| return rc; |
| |
| } |
| |
| /** Context extension */ |
| bcmos_errno bcmcli_extend(bcmcli_session *session, char *input_str, char *insert_str, uint32_t insert_size) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| bcmcli_entry *p_token; |
| bcmcli_name_value *pairs; |
| bcmos_bool last_is_space; |
| int npairs; |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| if (!mon_session || !mon_session->curdir || !input_str) |
| return BCM_ERR_PARM; |
| |
| insert_str[0] = 0; |
| mon_session->p_inbuf = input_str; |
| |
| last_is_space = strlen(input_str) && (input_str[strlen(input_str) - 1] == ' '); |
| |
| /* Split string to name/value pairs */ |
| rc = _bcmcli_split(mon_session, &pairs, &npairs); |
| if (rc) |
| return rc; |
| |
| /* empty list - display list of commands */ |
| if ( !npairs ) |
| { |
| _bcmcli_display_dir(mon_session, mon_session->curdir ); |
| BCMCLI_PARSE_RETURN(0); |
| } |
| |
| /* Identify parameters */ |
| switch (pairs[0].type) |
| { |
| case BCMCLI_TOKEN_NAME: |
| case BCMCLI_TOKEN_VALUE: |
| /* Identify command. The 1st pair can't contain name, only value */ |
| if (pairs[0].name || |
| !(p_token = _bcmcli_search_token(mon_session->curdir, pairs[0].value))) |
| { |
| _bcmcli_display_dir(mon_session, mon_session->curdir ); |
| BCMCLI_PARSE_RETURN(BCM_ERR_PARM); |
| } |
| |
| /* Directory or command ? */ |
| if (p_token->sel != BCMCLI_ENTRY_CMD) |
| BCMCLI_PARSE_RETURN(BCM_ERR_OK); |
| |
| /* Function token */ |
| mon_session->curcmd = p_token; |
| rc = _bcmcli_extend_parms(mon_session, &pairs[1], npairs-1, last_is_space, insert_str, insert_size); |
| break; |
| |
| default: |
| break; |
| } |
| |
| bcmcli_parse_out: |
| bcmos_free(pairs); |
| return rc; |
| } |
| |
| /** Parse and execute input string. |
| * input_string can contain multiple commands delimited by ';' |
| * |
| * \param[in] session Session handle |
| * \param[in] input_string String to be parsed. May consist of multiple ';'-delimited commands |
| * \return |
| * =0 - OK \n |
| * BCM_ERR_PARM - parsing error\n |
| * other - return code - as returned from command handler. |
| * It is recommended to return -EINTR to interrupt monitor loop. |
| */ |
| bcmos_errno bcmcli_parse(bcmcli_session *session, char* input_string) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| uint32_t input_len; |
| int rc = 0; |
| |
| if (!mon_session || !mon_session->curdir || !input_string) |
| return BCM_ERR_PARM; |
| input_len = strlen(input_string); |
| if (!input_len) |
| return 0; |
| |
| /* cut CR, LF if any */ |
| while (input_len && (input_string[input_len-1]=='\n' || input_string[input_len-1]=='\r')) |
| input_string[--input_len]=0; |
| |
| mon_session->p_inbuf = input_string; |
| mon_session->stop_monitor = 0; |
| |
| do { |
| rc = bcmcli_parse_command(session); |
| } while (mon_session->p_inbuf && mon_session->p_inbuf[0] && !mon_session->stop_monitor && !rc); |
| |
| return rc; |
| } |
| |
| /** Read input and parse iteratively until EOF or bcmcli_is_stopped() |
| * |
| * \param[in] session Session handle |
| * \return |
| * =0 - OK \n |
| */ |
| bcmos_errno bcmcli_driver(bcmcli_session *session) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| |
| session = mon_session->session; |
| mon_session->stop_monitor = 0; |
| while (!bcmcli_is_stopped(session) && |
| bcmcli_session_gets(session, mon_session->inbuf, sizeof(mon_session->inbuf)-1)) |
| { |
| /* Session could've been stopped while in "gets". Check again and proceed if active */ |
| if (!bcmcli_is_stopped(session)) |
| bcmcli_parse(session, mon_session->inbuf); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| |
| /* Stop monitor driver */ |
| void bcmcli_stop( bcmcli_session *session ) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| assert(mon_session); |
| mon_session->stop_monitor = 1; |
| } |
| |
| /** Returns 1 if monitor session is stopped |
| * \param[in] session Session handle |
| * \returns 1 if monitor session stopped by bcmcli_stop()\n |
| * 0 otherwise |
| */ |
| bcmos_bool bcmcli_is_stopped(bcmcli_session *session) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| return mon_session->stop_monitor; |
| } |
| |
| |
| /** Get parameter number given its name. |
| * The function is intended for use by command handlers |
| * \param[in] session Session handle |
| * \param[in,out] parm_name Parameter name |
| * \return |
| * >=0 - parameter number\n |
| * <0 - parameter with this name doesn't exist |
| */ |
| int bcmcli_parm_number(bcmcli_session *session, const char *parm_name) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| int i; |
| if (!parm_name || !mon_session || !mon_session->curcmd) |
| return BCM_ERR_PARM; |
| for(i=0; |
| mon_session->cmd_parms[i].name && |
| _bcmcli_stricmp( parm_name, mon_session->cmd_parms[i].name, -1); |
| i++) |
| ; |
| if (!mon_session->cmd_parms[i].name) |
| return BCM_ERR_PARM; |
| return i; |
| } |
| |
| |
| /** Get parameter by name |
| * The function is intended for use by command handlers |
| * \param[in] session Session handle |
| * \param[in] parm_name Parameter name |
| * \return |
| * parameter pointer or NULL if not found |
| */ |
| bcmcli_cmd_parm *bcmcli_parm_get(bcmcli_session *session, const char *parm_name) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| int nparm = bcmcli_parm_number(session, parm_name); |
| if (nparm < 0) |
| { |
| return NULL; |
| } |
| return &mon_session->cmd_parms[nparm]; |
| } |
| |
| |
| /** Check if parameter is set |
| * \param[in] session Session handle |
| * \param[in] parm_number Parameter number |
| * \return |
| * 1 if parameter is set\n |
| * 0 if parameter is not set or parm_number is invalid |
| */ |
| bcmos_errno bcmcli_parm_check(bcmcli_session *session, int parm_number) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| |
| if (parm_number < 0 || !mon_session || !mon_session->curcmd) |
| return BCM_ERR_PARM; |
| if (parm_number >= mon_session->num_parms) |
| return BCM_ERR_PARM; |
| if (!(mon_session->cmd_parms[parm_number].flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| return BCM_ERR_NOENT; |
| return BCM_ERR_OK; |
| } |
| |
| |
| /** Get enum's string value given its internal value |
| * \param[in] session Session handle |
| * \param[in] parm_number Parameter number |
| * \param[in] value Internal value |
| * \return |
| * enum string value or NULL if parameter is not enum or |
| * internal value is invalid |
| */ |
| const char *bcmcli_enum_parm_stringval(bcmcli_session *session, int parm_number, long value) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| int i; |
| bcmcli_enum_val *values; |
| if (parm_number < 0 || !mon_session || !mon_session->curcmd) |
| return NULL; |
| for(i=0; i<parm_number && mon_session->cmd_parms[i].name; i++) |
| ; |
| if (i < parm_number) |
| return NULL; |
| if (mon_session->cmd_parms[parm_number].type != BCMCLI_PARM_ENUM) |
| return NULL; |
| values = mon_session->cmd_parms[parm_number].enum_table; |
| return bcmcli_enum_stringval(values, value); |
| } |
| |
| |
| /* Get current directory handle */ |
| bcmcli_entry *bcmcli_dir_get(bcmcli_session *session) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| if (!mon_session) |
| return NULL; |
| return mon_session->curdir; |
| } |
| |
| /* Set current directory */ |
| bcmos_errno bcmcli_dir_set(bcmcli_session *session, bcmcli_entry *dir) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| assert(mon_session); |
| if (!mon_session) |
| return BCM_ERR_PARM; |
| /* Check access rights */ |
| if (!dir) |
| dir = _bcmcli_root_dir; |
| if (dir->access_right > bcmcli_session_access_right(mon_session->session)) |
| return BCM_ERR_PERM; |
| mon_session->curdir = dir; |
| return 0; |
| } |
| |
| /** Get token name |
| * \param[in] token Directory or command token |
| * \return directory token name |
| */ |
| const char *bcmcli_token_name(bcmcli_entry *token) |
| { |
| if (!token) |
| return NULL; |
| return token->name; |
| } |
| |
| bcmcli_cmd_parm *bcmcli_find_named_parm(bcmcli_session *session, const char *name) |
| { |
| bcmcli_cmd_parm * cmd_parm; |
| |
| if ( !session || !name || *name=='\0') |
| return NULL; |
| |
| cmd_parm = _bcmcli_find_named_parm(_bcmcli_session_data(session), name); |
| if(cmd_parm && (cmd_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| { |
| return cmd_parm; |
| } |
| |
| return NULL; |
| } |
| |
| /* Return TRUE if parameter value is set */ |
| bcmos_bool bcmcli_parm_value_is_set(bcmcli_session *session, bcmcli_cmd_parm *parm, uint32_t value_index) |
| { |
| bcmcli_session_extras *mon_session=_bcmcli_session_data(session); |
| uint32_t parm_index = parm - mon_session->cmd_parms; |
| |
| if (!mon_session) |
| { |
| bcmcli_print(NULL, "MON> Session %p is invalid\n", session); |
| return BCMOS_FALSE; |
| } |
| |
| parm_index = parm - mon_session->cmd_parms; |
| if (parm_index >= BCMCLI_MAX_PARMS) |
| { |
| bcmcli_print(session, "MON> Parameter %p is invalid\n", parm); |
| return BCMOS_FALSE; |
| } |
| |
| if (!parm->array_size) |
| return mon_session->value_status[parm_index].value_set; |
| |
| if (value_index >= parm->array_size) |
| return BCMOS_FALSE; |
| |
| return mon_session->value_status[parm_index].values_set[value_index]; |
| } |
| |
| bcmcli_cmd_parm *bcmcli_find_parm_by_prefix(bcmcli_session *session, const char *prefix) |
| { |
| bcmcli_cmd_parm *cmd_parm; |
| |
| if (session == NULL || prefix == NULL) |
| { |
| return NULL; |
| } |
| |
| cmd_parm = _bcmcli_session_data(session)->cmd_parms; |
| while (cmd_parm->name != NULL) |
| { |
| if ((_bcmcli_stricmp(prefix, cmd_parm->name, strlen(prefix)) == 0) && |
| ((cmd_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED) != 0)) |
| { |
| return cmd_parm; |
| } |
| ++cmd_parm; |
| } |
| |
| return NULL; |
| } |
| |
| /** Print CLI parameter |
| * \param[in] session Session handle |
| * \param[in] parm Parameter |
| */ |
| void bcmcli_parm_print(bcmcli_session *session, const bcmcli_cmd_parm *parm) |
| { |
| char buf[BCMCLI_MAX_PARM_VAL_LEN] = ""; |
| if (parm->type != BCMCLI_PARM_BUFFER) |
| { |
| parm->format_cb(parm, parm->value, buf, sizeof(buf)); |
| bcmcli_print(session, "%s\n", buf); |
| } |
| else |
| { |
| if (parm->value.buffer.len == 0) |
| { |
| bcmcli_print(session, "-\n"); |
| } |
| else |
| { |
| bcmcli_print(session, "\n"); |
| bcmcli_session_hexdump(session, parm->value.buffer.start, 0, |
| bcmolt_buf_get_used(&parm->value.buffer), NULL); |
| } |
| } |
| } |
| |
| /** Enable / disable CLI command logging |
| * \param[in] mode Logging flags |
| * \param[in] log Log session. Must be set if mode != BCMCLI_CMD_LOG_NONE |
| * \return 0=OK or error <0 |
| */ |
| bcmos_errno bcmcli_log_set(bcmcli_log_mode mode, bcmcli_session *log) |
| { |
| if (mode != BCMCLI_LOG_NONE && log == NULL) |
| { |
| BCMOS_TRACE_ERR("log session must be set\n"); |
| return BCM_ERR_PARM; |
| } |
| if (mode == BCMCLI_LOG_NONE) |
| { |
| _bcmcli_log_session = NULL; |
| } |
| else |
| { |
| _bcmcli_log_session = log; |
| } |
| _bcmcli_log_mode = mode; |
| return BCM_ERR_OK; |
| } |
| |
| /** Write string to CLI log. |
| * The function is ignored if CLI logging is not enabled using bcmcli_log_set() |
| * \param[in] format printf-like format followed by arguments |
| */ |
| void bcmcli_log(const char *format, ...) |
| { |
| va_list ap; |
| if (!_bcmcli_log_session) |
| return; |
| va_start(ap, format); |
| bcmcli_session_vprint(_bcmcli_log_session, format, ap); |
| va_end(ap); |
| } |
| |
| /*********************************************************/ |
| /* Internal functions */ |
| /*********************************************************/ |
| |
| static void _bcmcli_log_cmd(const char *cmd) |
| { |
| switch (_bcmcli_log_mode) |
| { |
| case BCMCLI_LOG_CLI: |
| bcmcli_log("%s\n", cmd); |
| break; |
| case BCMCLI_LOG_C_COMMENT: |
| bcmcli_log("/* %s */\n", cmd); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void _bcmcli_log_rc(bcmos_errno rc) |
| { |
| switch (_bcmcli_log_mode) |
| { |
| case BCMCLI_LOG_CLI: |
| bcmcli_log("# CLI command completed: %s (%d)\n", bcmos_strerror(rc), rc); |
| break; |
| case BCMCLI_LOG_C_COMMENT: |
| bcmcli_log("/* CLI command completed: %s (%d) */\n", bcmos_strerror(rc), rc); |
| break; |
| default: |
| break; |
| } |
| |
| } |
| |
| static bcmcli_session *_bcmcli_help_session_open(bcmcli_session *main_session) |
| { |
| bcmolt_string *help_scratch; |
| bcmcli_session *help_session; |
| bcmos_errno err; |
| |
| bcmolt_string_create(&help_scratch, BCMCLI_HELP_BUFFER_SIZE); |
| err = bcmcli_session_open_string(&help_session, help_scratch); |
| if (err) |
| { |
| bcmcli_session_print(main_session, "CLI: can't create help session. Error %s\n", bcmos_strerror(err)); |
| return NULL; |
| } |
| |
| return help_session; |
| } |
| |
| static void _bcmcli_help_session_print_and_close(bcmcli_session *main_session, bcmcli_session *help_session) |
| { |
| bcmolt_string *str = bcmcli_session_user_priv(help_session); |
| |
| bcmcli_session_print(main_session, "%s", bcmolt_string_get(str)); |
| bcmcli_session_close(help_session); |
| bcmolt_string_destroy(str); |
| } |
| |
| #ifdef CONFIG_LINENOISE |
| static bcmos_errno _bcmcli_line_edit_cmd(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms) |
| { |
| if (n_parms > 0) |
| { |
| if ((parm[0].flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| linenoiseSetDumbTerminal(session->ln_session, ! parm[0].value.number); |
| if ((parm[1].flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| linenoiseSetMultiLine(session->ln_session, parm[1].value.number); |
| } |
| else |
| { |
| int dumb = linenoiseGetDumbTerminal(session->ln_session); |
| int multiline = linenoiseGetMultiLine(session->ln_session); |
| bcmcli_session_print(session, "Line editing: %s Multiline: %s\n", |
| dumb ? "off" : "on", multiline ? "on" : "off"); |
| } |
| return BCM_ERR_OK; |
| } |
| #endif |
| |
| /* Allocate root directory and default session */ |
| static void _bcmcli_alloc_root(const bcmcli_session_parm *first_session_parm) |
| { |
| bcmcli_session_parm session_parms; |
| bcmcli_session *session; |
| int rc; |
| |
| /* The very first call. Allocate root structure */ |
| if ((_bcmcli_root_dir=(bcmcli_entry *)bcmos_calloc(sizeof(bcmcli_entry) + strlen(BCMCLI_ROOT_HELP) + 2 )) == NULL) |
| return; |
| _bcmcli_root_dir->name = (char *)(_bcmcli_root_dir + 1); |
| _bcmcli_root_dir->help = (char *)(_bcmcli_root_dir->name + 1); |
| strcpy(_bcmcli_root_dir->help, BCMCLI_ROOT_HELP); |
| _bcmcli_root_dir->sel = BCMCLI_ENTRY_DIR; |
| _bcmcli_root_dir->access_right = BCMCLI_ACCESS_GUEST; |
| |
| memset(&session_parms, 0, sizeof(session_parms)); |
| session_parms.access_right = BCMCLI_ACCESS_ADMIN; |
| session_parms.extra_size = sizeof(bcmcli_session_extras); |
| session_parms.name = "monroot"; |
| if (first_session_parm) |
| { |
| session_parms.line_edit_mode = first_session_parm->line_edit_mode; |
| } |
| rc = bcmcli_session_open(&session_parms, &session); |
| if (rc) |
| { |
| bcmos_free(_bcmcli_root_dir); |
| _bcmcli_root_dir = NULL; |
| _bcmcli_root_session = NULL; |
| return; |
| } |
| _bcmcli_root_session = _bcmcli_session_data(session); |
| _bcmcli_root_session->session = session; |
| _bcmcli_root_session->curdir = _bcmcli_root_dir; |
| |
| /* Add command to disable/enable line editing */ |
| #ifdef CONFIG_LINENOISE |
| if (session_parms.line_edit_mode != BCMCLI_LINE_EDIT_DISABLE) |
| { |
| BCMCLI_MAKE_CMD(NULL, "~", "Enable/disable/query line editing", _bcmcli_line_edit_cmd, |
| BCMCLI_MAKE_PARM_ENUM("enable", "Enable line editing", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL), |
| BCMCLI_MAKE_PARM_ENUM("multiline", "Enable multiline mode", bcmcli_enum_bool_table, |
| BCMCLI_PARM_FLAG_OPTIONAL)); |
| } |
| #endif |
| } |
| |
| /* Display directory */ |
| static void _bcmcli_display_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir) |
| { |
| bcmcli_session *session = mon_session->session; |
| bcmcli_entry *p_token; |
| bcmcli_entry *prev=NULL; |
| bcmcli_session *help_session = _bcmcli_help_session_open(session); |
| |
| if (!help_session) |
| return; |
| |
| bcmcli_session_print(help_session, "%s%s> ", (p_dir==_bcmcli_root_dir)?"":".../", p_dir->name ); |
| p_token = p_dir->u.dir.first; |
| while ( p_token ) |
| { |
| if (p_token->access_right <= bcmcli_session_access_right(session)) |
| { |
| if (prev) |
| bcmcli_session_print(help_session, ", "); |
| bcmcli_session_print(help_session, "%s", p_token->name ); |
| if (p_token->sel == BCMCLI_ENTRY_DIR ) |
| bcmcli_session_print(help_session, "/"); |
| prev = p_token; |
| } |
| p_token = p_token->next; |
| } |
| bcmcli_session_print(help_session, "\n"); |
| _bcmcli_help_session_print_and_close(session, help_session); |
| } |
| |
| |
| /* Is character that can be used in a single token ? */ |
| static inline int _bcmcli_is_special_char(char c) |
| { |
| if (!c) |
| return 0; |
| return (c == BCMCLI_HELP_CHAR || c == BCMCLI_COMMENT_CHAR || c == BCMCLI_EQUAL_CHAR); |
| } |
| |
| /* Make a preliminary analizis of <name> token. |
| * Returns a token type (Empty, Up, Root, Break, Name) |
| */ |
| static bcmcli_token_type _bcmcli_analyze_token( const char *name ) |
| { |
| if (!name[0] || name[0]==';') |
| return BCMCLI_TOKEN_EMPTY; |
| |
| if (*name == BCMCLI_COMMENT_CHAR) |
| return BCMCLI_TOKEN_BREAK; |
| |
| if (!strcmp(name, BCMCLI_UP_STR)) |
| return BCMCLI_TOKEN_UP; |
| |
| if (!strcmp(name, BCMCLI_ROOT_STR)) |
| return BCMCLI_TOKEN_ROOT; |
| |
| if (*name == BCMCLI_HELP_CHAR) |
| return BCMCLI_TOKEN_HELP; |
| |
| return BCMCLI_TOKEN_VALUE; |
| |
| } |
| |
| |
| /* isspace wrapper */ |
| static inline int _bcmcli_isspace(char c) |
| { |
| return isspace((int)c); |
| } |
| |
| /* Cut the first word from <p_inbuf>. |
| * - Return pointer to start of the word in p_word |
| * - 0 terminator is inserted in the end of the word |
| * - session->p_inbuf is updated to point after the word |
| * Returns token type |
| */ |
| static bcmcli_token_type _bcmcli_get_word(bcmcli_session_extras *mon_session, char **buf, char **p_word) |
| { |
| bcmcli_token_type token_type; |
| char *p_inbuf = *buf; |
| char next_char = 0; |
| bcmos_bool quoted_string = BCMOS_FALSE; |
| |
| /* Skip leading blanks */ |
| while (*p_inbuf && (_bcmcli_isspace(*p_inbuf) || (*p_inbuf==','))) |
| ++p_inbuf; |
| |
| *buf = p_inbuf; |
| if (! *p_inbuf) |
| return BCMCLI_TOKEN_EMPTY; |
| if (*p_inbuf == ';') |
| { |
| *p_inbuf = 0; |
| *buf = ++p_inbuf; |
| return BCMCLI_TOKEN_EMPTY; |
| } |
| |
| /* Quoted string ? */ |
| if (*p_inbuf == '"') |
| { |
| quoted_string = BCMOS_TRUE; |
| *p_word = ++p_inbuf; |
| while ( *p_inbuf && *p_inbuf!='"' ) |
| ++p_inbuf; |
| if (*p_inbuf != '"') |
| { |
| bcmcli_session_print(mon_session->session, "MON: unterminated string %s\n", *p_word); |
| return BCMCLI_TOKEN_EMPTY; |
| } |
| if (*p_inbuf) |
| *(p_inbuf++) = 0; |
| } |
| else |
| { |
| *p_word = p_inbuf; |
| if (!_bcmcli_is_special_char(*p_inbuf)) |
| { |
| do ++p_inbuf; |
| while (*p_inbuf && !_bcmcli_isspace(*p_inbuf) && *p_inbuf!=';' && !_bcmcli_is_special_char(*p_inbuf)); |
| /* Skip trailing spaces */ |
| while (*p_inbuf && _bcmcli_isspace(*p_inbuf)) |
| *(p_inbuf++) = 0; |
| next_char = *p_inbuf; |
| if (next_char == BCMCLI_EQUAL_CHAR) |
| *(p_inbuf++) = 0; |
| } |
| else |
| { |
| ++p_inbuf; |
| } |
| } |
| *buf = p_inbuf; |
| token_type = _bcmcli_analyze_token( *p_word ); |
| if (token_type == BCMCLI_TOKEN_VALUE && next_char == BCMCLI_EQUAL_CHAR) |
| token_type = BCMCLI_TOKEN_NAME; |
| if ((token_type == BCMCLI_TOKEN_EMPTY) && quoted_string) |
| token_type = BCMCLI_TOKEN_VALUE; |
| return token_type; |
| } |
| |
| /* Split string to [name=]value pairs */ |
| static bcmos_errno _bcmcli_split(bcmcli_session_extras *mon_session, bcmcli_name_value **p_pairs, int *p_npairs) |
| { |
| bcmcli_name_value *pairs; |
| char *tmp_buf, *tmp_buf_org; |
| char *word; |
| bcmcli_token_type token_type, prev_type=BCMCLI_TOKEN_EMPTY; |
| int n = 0; |
| |
| /* Make a copy of input buffer */ |
| tmp_buf_org = tmp_buf = bcmos_alloc(strlen(mon_session->p_inbuf) + 1); |
| if (!tmp_buf) |
| return BCM_ERR_NOMEM; |
| strcpy(tmp_buf, mon_session->p_inbuf); |
| |
| /* Calculate number of pairs first */ |
| token_type = _bcmcli_get_word(mon_session, &tmp_buf, &word); |
| while (token_type != BCMCLI_TOKEN_EMPTY && token_type != BCMCLI_TOKEN_BREAK) |
| { |
| /* Skip =value */ |
| if (!(prev_type == BCMCLI_TOKEN_NAME && token_type == BCMCLI_TOKEN_VALUE)) |
| ++n; |
| prev_type = token_type; |
| token_type = _bcmcli_get_word(mon_session, &tmp_buf, &word); |
| } |
| bcmos_free(tmp_buf_org); |
| *p_npairs = n; |
| if (!n) |
| { |
| *p_pairs = NULL; |
| /* Cut input string in order to prevent infinite loop in the parser if the string |
| * is not empty (e.g., contains spaces) */ |
| *mon_session->p_inbuf = 0; |
| if (token_type == BCMCLI_TOKEN_BREAK) |
| { |
| return BCM_ERR_NOENT; |
| } |
| return 0; |
| } |
| |
| *p_pairs = pairs = bcmos_calloc(n * sizeof(bcmcli_name_value)); |
| if (! pairs) |
| return BCM_ERR_NOMEM; |
| |
| /* Now scan the original string and set names and values */ |
| token_type = _bcmcli_get_word(mon_session, &mon_session->p_inbuf, &word); |
| prev_type=BCMCLI_TOKEN_EMPTY; |
| --pairs; /* it is going to be pre-incremented */ |
| while (token_type != BCMCLI_TOKEN_EMPTY && token_type != BCMCLI_TOKEN_BREAK) |
| { |
| if (!(prev_type == BCMCLI_TOKEN_NAME && token_type == BCMCLI_TOKEN_VALUE)) |
| ++pairs; |
| pairs->type = token_type; |
| if (token_type == BCMCLI_TOKEN_NAME) |
| { |
| pairs->name = word; |
| } |
| else |
| { |
| pairs->value = word; |
| } |
| prev_type = token_type; |
| token_type = _bcmcli_get_word(mon_session, &mon_session->p_inbuf, &word); |
| } |
| return 0; |
| } |
| |
| /* Find parameter by name */ |
| static bcmcli_cmd_parm *_bcmcli_find_named_parm(bcmcli_session_extras *mon_session, const char *name) |
| { |
| bcmcli_cmd_parm *cmd_parm = mon_session->cmd_parms; |
| |
| while (cmd_parm->name) |
| { |
| if (!_bcmcli_stricmp(name, cmd_parm->name, -1)) |
| break; |
| ++cmd_parm; |
| } |
| |
| if (!cmd_parm->name) |
| return NULL; |
| |
| return cmd_parm; |
| } |
| |
| /* Extend session parameter table based on selector value */ |
| static bcmos_errno _bcmcli_extend_parm_table(bcmcli_session_extras *mon_session, |
| bcmcli_cmd_parm *selector, const char *value) |
| { |
| bcmcli_enum_val *values=selector->enum_table; |
| bcmcli_cmd_parm *parms; |
| bcmcli_cmd_parm *session_parm; |
| int nsel = selector - mon_session->cmd_parms; |
| bcmcli_parm_value_status *val_status; |
| int nparms; |
| |
| while (values->name) |
| { |
| if (!_bcmcli_stricmp(values->name, value, -1)) |
| break; |
| ++values; |
| } |
| if (!values->name) |
| return BCM_ERR_INTERNAL; |
| |
| /* Calculate number of parameters in selected table */ |
| parms = values->parms; |
| while (parms && parms->name) |
| { |
| ++parms; |
| } |
| nparms = parms - values->parms; |
| |
| if (mon_session->num_parms + nparms >= BCMCLI_MAX_PARMS) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> Can's process selector %s. Too many parameters\n", |
| mon_session->curcmd->name, selector->name); |
| return BCM_ERR_OVERFLOW; |
| } |
| |
| /* Shift session parameters making room for the new table */ |
| if (selector != &mon_session->cmd_parms[mon_session->num_parms-1]) |
| { |
| memmove(selector + nparms + 1, selector + 1, |
| (&mon_session->cmd_parms[mon_session->num_parms-1] - selector) * sizeof(bcmcli_cmd_parm)); |
| memmove(&mon_session->value_status[nsel + nparms + 1], &mon_session->value_status[nsel + 1], |
| (mon_session->num_parms - nsel) * sizeof(bcmcli_parm_value_status)); |
| } |
| |
| /* Finally insert selector's table */ |
| parms = values->parms; |
| session_parm = selector+1; |
| val_status = &mon_session->value_status[nsel + 1]; |
| while (parms && parms->name) |
| { |
| *session_parm = *parms; |
| _bcmcli_assign_callbacks(session_parm); |
| |
| if (parms->max_array_size) |
| { |
| val_status->values_set = bcmos_calloc(sizeof(bcmos_bool) * parms->max_array_size); |
| if (!val_status->values_set) |
| { |
| bcmcli_session_print(mon_session->session, "MON: > Couldn't allocate value status array for %s\n", |
| parms->name); |
| return BCM_ERR_NOMEM; |
| } |
| } |
| else |
| { |
| val_status->value_set = BCMOS_FALSE; |
| } |
| |
| |
| ++parms; |
| ++session_parm; |
| ++val_status; |
| } |
| mon_session->num_parms += nparms; |
| |
| return BCM_ERR_OK; |
| } |
| |
| /* Parse a single parameter value (scalar value or array element) */ |
| static bcmos_errno _bcmcli_parse_1value(bcmcli_session_extras *mon_session, bcmcli_entry *cmd, |
| bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_value, |
| int val_len, bcmos_bool suppress_err_print) |
| { |
| bcmos_errno rc; |
| |
| if (val_len >= 0) |
| { |
| /* We are dealing with array element. string_value is comma rather than |
| * 0-terminated. Copy it aside. |
| */ |
| char val_copy[val_len + 1]; |
| strncpy(val_copy, string_value, val_len); |
| val_copy[val_len] = 0; |
| rc = parm->scan_cb(parm, value, val_copy); |
| } |
| else |
| { |
| rc = parm->scan_cb(parm, value, string_value); |
| } |
| if (rc) |
| { |
| if (!suppress_err_print) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> <%s>: value %s is invalid\n", |
| cmd->name, parm->name, string_value); |
| } |
| } |
| return rc; |
| } |
| |
| |
| /* Parse parameter value, including array value (comma-delimited list of element values) */ |
| static bcmos_errno _bcmcli_parse_value(bcmcli_session_extras *mon_session, bcmcli_entry *cmd, |
| bcmcli_cmd_parm *parm, const char *string_value, bcmos_bool suppress_err_print) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| uint32_t parm_index = parm - mon_session->cmd_parms; |
| |
| BUG_ON(parm_index >= BCMCLI_MAX_PARMS); |
| |
| if (parm->max_array_size) |
| { |
| uint32_t i = 0; |
| |
| /* Empty array? */ |
| if (_bcmcli_stricmp(string_value, BCMCLI_ARRAY_EMPTY, -1)) |
| { |
| /* array element values are comma-delimited */ |
| for (i = 0; i < parm->max_array_size && string_value && *string_value && !rc; i++) |
| { |
| const char *pcomma; |
| int val_len; |
| |
| pcomma = strchr(string_value, BCMCLI_ARRAY_DELIM_CHAR); |
| if (pcomma) |
| { |
| val_len = pcomma - string_value; |
| } |
| else |
| { |
| val_len = -1; /* to the end of string */ |
| } |
| /* No value ? */ |
| if (_bcmcli_stricmp(string_value, BCMCLI_PARM_NO_VALUE, val_len)) |
| { |
| rc = _bcmcli_parse_1value(mon_session, cmd, |
| parm, &parm->values[i], string_value, val_len, suppress_err_print); |
| mon_session->value_status[parm_index].values_set[i] = (rc == BCM_ERR_OK); |
| } |
| string_value = pcomma ? pcomma + 1 : NULL; |
| } |
| /* If all parsed values were ok, but we have more values than array size - it is an error */ |
| if (string_value && *string_value && !rc) |
| { |
| rc = BCM_ERR_TOO_MANY; |
| if (!suppress_err_print) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> <%s>: too many values. %s is invalid\n", |
| cmd->name, parm->name, string_value); |
| } |
| } |
| } |
| |
| parm->array_size = i; |
| } |
| else |
| { |
| if (_bcmcli_stricmp(string_value, BCMCLI_PARM_NO_VALUE, strlen(string_value))) |
| { |
| rc = _bcmcli_parse_1value(mon_session, cmd, |
| parm, &parm->value, string_value, -1, suppress_err_print); |
| mon_session->value_status[parm_index].value_set = (rc == BCM_ERR_OK); |
| } |
| } |
| |
| return rc; |
| } |
| |
| /* Release value status arrays */ |
| static void _bcmcli_free_session_value_status(bcmcli_session_extras *mon_session) |
| { |
| bcmcli_cmd_parm *parms=mon_session->cmd_parms; |
| int i; |
| |
| for (i = 0; i < BCMCLI_MAX_PARMS; i++) |
| { |
| if (parms[i].max_array_size) |
| { |
| if (mon_session->value_status[i].values_set != NULL) |
| { |
| bcmos_free(mon_session->value_status[i].values_set); |
| mon_session->value_status[i].values_set = NULL; |
| } |
| } |
| else |
| { |
| mon_session->value_status[i].value_set = BCMOS_FALSE; |
| } |
| } |
| } |
| |
| /* Populate session parameters. Apply selectors */ |
| static bcmos_errno _bcmcli_populate_parms(bcmcli_session_extras *mon_session, bcmcli_entry *cmd, |
| bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print, int *last) |
| { |
| const char *parm_value; |
| int positional=1; |
| bcmcli_cmd_parm *parms=mon_session->cmd_parms; |
| bcmcli_cmd_parm *cur_parm; |
| int rc; |
| int i; |
| |
| /* Mark all parameters as don't having an explicit value */ |
| memset(&parms[0], 0, sizeof(mon_session->cmd_parms)); |
| memcpy(&parms[0], cmd->u.cmd.parms, sizeof(bcmcli_cmd_parm)*cmd->u.cmd.num_parms); |
| /* Clear array buffers */ |
| for (i = 0; i < cmd->u.cmd.num_parms; i++) |
| { |
| if (parms[i].max_array_size) |
| { |
| BUG_ON(!parms[i].values); |
| memset(parms[i].values, 0, sizeof(bcmcli_parm_value) * parms[i].max_array_size); |
| mon_session->value_status[i].values_set = bcmos_calloc(sizeof(bcmos_bool) * parms[i].max_array_size); |
| if (!mon_session->value_status[i].values_set) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> Couldn't allocate value status array for %s\n", |
| cmd->name, pairs[i].name); |
| return BCM_ERR_NOMEM; |
| } |
| } |
| else |
| { |
| mon_session->value_status[i].value_set = BCMOS_FALSE; |
| } |
| } |
| mon_session->curcmd = cmd; |
| mon_session->num_parms = cmd->u.cmd.num_parms; |
| if (last) |
| *last = 0; |
| /* Build a format string */ |
| for (i=0; i<npairs && pairs[i].type != BCMCLI_TOKEN_BREAK; i++) |
| { |
| parm_value = pairs[i].value; |
| if (last) |
| *last = i; |
| cur_parm = NULL; |
| /* Named parameter ? */ |
| if (pairs[i].name) |
| { |
| if ( (cmd->u.cmd.extras.flags & BCMCLI_CMD_FLAG_NO_NAME_PARMS) ) |
| { |
| if (!suppress_err_print) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> Doesn't support named parameters. %s is unexpected\n", |
| cmd->name, pairs[i].name); |
| } |
| return BCM_ERR_PARM; |
| } |
| positional = 0; /* No more positional parameters */ |
| /* Check name */ |
| cur_parm = _bcmcli_find_named_parm(mon_session, pairs[i].name); |
| if (!cur_parm) |
| { |
| if (!suppress_err_print) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> parameter <%s> doesn't exist\n", |
| cmd->name, pairs[i].name); |
| } |
| return BCM_ERR_PARM; |
| } |
| if (!parm_value) |
| { |
| if (!suppress_err_print) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> <%s>: value is missing\n", |
| cmd->name, cur_parm->name); |
| } |
| return BCM_ERR_PARM; |
| } |
| } |
| else |
| { |
| /* it can still be named ENUM parameter (without =value). In this case the 1st |
| * enum value is assumed. Check it |
| */ |
| if (parm_value && (cur_parm = _bcmcli_find_named_parm(mon_session, parm_value)) && |
| (cur_parm->type == BCMCLI_PARM_ENUM)) |
| { |
| pairs[i].name = parm_value; |
| pairs[i].value = parm_value = cur_parm->enum_table->name; |
| positional = 0; /* No more positional parameters */ |
| } |
| else |
| { |
| if (!positional) |
| { |
| if (!suppress_err_print) |
| bcmcli_session_print(mon_session->session, "MON: %s> Expected named parameter. Got %s\n", cmd->name, parm_value); |
| return BCM_ERR_PARM; |
| } |
| cur_parm = &parms[i]; |
| } |
| if (!cur_parm->name) |
| { |
| if (!suppress_err_print) |
| bcmcli_session_print(mon_session->session, "MON: %s> Too many parameters. %s is unexpected\n", cmd->name, parm_value); |
| return BCM_ERR_PARM; |
| } |
| } |
| |
| if (cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED) |
| { |
| if (!suppress_err_print) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> Attempt to assign parameter %s more than once\n", |
| cmd->name, cur_parm->name); |
| } |
| return BCM_ERR_PARM; |
| } |
| |
| if (parm_value) |
| { |
| if (cur_parm->type == BCMCLI_PARM_STRING) |
| cur_parm->value.string = parm_value; |
| else |
| { |
| rc = _bcmcli_parse_value(mon_session, cmd, cur_parm, parm_value, suppress_err_print); |
| if (rc) |
| return rc; |
| |
| /* For parameter-selector extend list of parameters accordingly */ |
| if (cur_parm->flags & BCMCLI_PARM_FLAG_SELECTOR) |
| { |
| rc = _bcmcli_extend_parm_table(mon_session, cur_parm, parm_value); |
| if (rc) |
| return rc; |
| } |
| } |
| cur_parm->flags |= BCMCLI_PARM_FLAG_ASSIGNED; |
| } |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /* Parse p_inbuf string based on parameter descriptions in <p_token>. |
| * Fill parameter values in <p_token>. |
| * Returns the number of parameters filled or BCM_ERR_PARM |
| * To Do: add a option of one-by-one user input of missing parameters. |
| */ |
| static int _bcmcli_parse_parms( bcmcli_session_extras *mon_session, bcmcli_entry *cmd, bcmcli_name_value *pairs, int npairs) |
| { |
| bcmcli_cmd_parm *parms=mon_session->cmd_parms; |
| int rc; |
| int i; |
| |
| /* Populate parameter table */ |
| rc = _bcmcli_populate_parms(mon_session, cmd, pairs, npairs, BCMOS_FALSE, NULL); |
| if (rc) |
| goto err_return; |
| |
| |
| rc = BCM_ERR_PARM; |
| |
| /* Make sure that parameters are OK. Check range, process default values */ |
| for (i=0; i<mon_session->num_parms; i++) |
| { |
| bcmcli_cmd_parm *cur_parm = &parms[i]; |
| |
| if (!(cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| { |
| if ((cur_parm->flags & BCMCLI_PARM_FLAG_DEFVAL)) |
| { |
| cur_parm->flags |= BCMCLI_PARM_FLAG_ASSIGNED; |
| } |
| else if (!(cur_parm->flags & BCMCLI_PARM_FLAG_OPTIONAL) ) |
| { |
| /* Mandatory parameter missing */ |
| bcmcli_session_print(mon_session->session, "MON: %s> Mandatory parameter <%s> is missing\n", cmd->name, parms[i].name); |
| goto err_return; |
| } |
| } |
| |
| /* Check value */ |
| if ((cur_parm->flags & BCMCLI_PARM_FLAG_RANGE)) |
| { |
| if ((cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| { |
| if (cur_parm->array_size) |
| { |
| uint32_t j; |
| |
| for (j = 0; j < cur_parm->array_size; j++) |
| { |
| if (((cur_parm->values[j].number < cur_parm->low_val) || |
| (cur_parm->values[j].number > cur_parm->hi_val))) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> <%s>: %ld out of range (%ld, %ld)\n", |
| cmd->name, cur_parm->name, cur_parm->values[j].number, cur_parm->low_val, cur_parm->hi_val); |
| goto err_return; |
| } |
| } |
| } |
| else if (((cur_parm->value.number < cur_parm->low_val) || |
| (cur_parm->value.number > cur_parm->hi_val))) |
| { |
| bcmcli_session_print(mon_session->session, "MON: %s> <%s>: %ld out of range (%ld, %ld)\n", |
| cmd->name, cur_parm->name, cur_parm->value.number, cur_parm->low_val, cur_parm->hi_val); |
| goto err_return; |
| } |
| } |
| } |
| } |
| |
| return BCM_ERR_OK; |
| |
| err_return: |
| _bcmcli_free_session_value_status(mon_session); |
| return rc; |
| } |
| |
| /* insert value skipping partial match trhat is already present */ |
| static void _bcmcli_insert(const char *partial_match, const char *insert_val1, |
| const char *insert_val2, char *insert_str, uint32_t insert_size) |
| { |
| if (partial_match) |
| insert_val1 += strlen(partial_match); |
| bcmcli_strncpy(insert_str, insert_val1, insert_size); |
| if (insert_val2) |
| bcmcli_strncat(insert_str, insert_val2, insert_size); |
| } |
| |
| static void _bcmcli_update_longest_match(char *longest_match, const char *name) |
| { |
| uint32_t nlen = strlen(name); |
| uint32_t lmlen = strlen(longest_match); |
| |
| if (nlen < lmlen) |
| { |
| lmlen = nlen; |
| } |
| while (lmlen && memcmp(longest_match, name, lmlen)) |
| { |
| --lmlen; |
| } |
| longest_match[lmlen] = 0; |
| } |
| |
| |
| /* extend value. |
| * If !enum - do nothing |
| * If more than 1 matching value - display them |
| * If no matching value - do nothing |
| * If 1 matching value - insert |
| */ |
| static void _bcmcli_extend_value(bcmcli_session_extras *mon_session, bcmcli_cmd_parm *parm, |
| const char *partial_value, char *insert_str, uint32_t insert_size) |
| { |
| int nmatch = 0; |
| bcmcli_enum_val *vals = parm->enum_table; |
| char longest_match[BCMCLI_MAX_SEARCH_SUBSTR_LENGTH]=""; |
| |
| if ((parm->type != BCMCLI_PARM_ENUM && parm->type != BCMCLI_PARM_ENUM_MASK) || !vals) |
| return; |
| |
| /* If enum mask, partial value can be a sum of values. Skip past the last '+' sign */ |
| if (parm->type == BCMCLI_PARM_ENUM_MASK && partial_value) |
| { |
| char *pdel = strrchr(partial_value, BCMCLI_ENUM_MASK_DEL_CHAR); |
| if (pdel) |
| partial_value = pdel + 1; |
| } |
| |
| while (vals->name) |
| { |
| if (!partial_value || !strncmp(vals->name, partial_value, strlen(partial_value))) |
| { |
| if (!nmatch) |
| { |
| bcmcli_strncpy(longest_match, vals->name, sizeof(longest_match)); |
| } |
| else |
| { |
| _bcmcli_update_longest_match(longest_match, vals->name); |
| } |
| ++nmatch; |
| } |
| ++vals; |
| } |
| if (!nmatch) |
| return; |
| if (nmatch == 1) |
| { |
| _bcmcli_insert(partial_value, longest_match, " ", insert_str, insert_size); |
| return; |
| } |
| /* display all matching values */ |
| _bcmcli_insert(partial_value, longest_match, "", insert_str, insert_size); |
| bcmcli_session_print(mon_session->session, "\n"); |
| vals = parm->enum_table; |
| while (vals->name) |
| { |
| if (!partial_value || !strncmp(vals->name, partial_value, strlen(partial_value))) |
| bcmcli_session_print(mon_session->session, " %s", vals->name); |
| ++vals; |
| } |
| bcmcli_session_print(mon_session->session, "\n"); |
| } |
| |
| /* calculate number of matching parameter names */ |
| static int _bcmcli_num_matching_names(bcmcli_session_extras *mon_session, const char *partial_value, int *first_match) |
| { |
| int i; |
| int nmatch = 0; |
| |
| *first_match = -1; |
| for (i = 0; i < mon_session->num_parms; i++) |
| { |
| uint32_t flags = mon_session->cmd_parms[i].flags; |
| if ((flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| continue; |
| if (partial_value && strncmp(mon_session->cmd_parms[i].name, partial_value, strlen(partial_value))) |
| continue; |
| if (*first_match == -1) |
| *first_match = i; |
| ++nmatch; |
| } |
| return nmatch; |
| } |
| |
| /* calculate longest matching string. |
| * returns number of matching parameters |
| */ |
| static int _bcmcli_longest_match(bcmcli_session_extras *mon_session, const char *partial_value, |
| char *longest_match, uint32_t longest_match_size, int *first_match) |
| { |
| int nmatch0 = _bcmcli_num_matching_names(mon_session, partial_value, first_match); |
| int nmatch; |
| const char *match_name; |
| |
| if (!nmatch0) |
| return nmatch0; |
| match_name = mon_session->cmd_parms[*first_match].name; |
| if (nmatch0 == 1) |
| { |
| bcmcli_strncpy(longest_match, match_name, longest_match_size); |
| return nmatch0; |
| } |
| bcmcli_strncpy(longest_match, match_name, longest_match_size); |
| nmatch = _bcmcli_num_matching_names(mon_session, longest_match, first_match); |
| while (nmatch != nmatch0) |
| { |
| longest_match[strlen(longest_match)-1] = 0; |
| nmatch = _bcmcli_num_matching_names(mon_session, longest_match, first_match); |
| } |
| return nmatch0; |
| } |
| |
| /* display/insert unset matching names |
| * If more than 1 matching value - display them |
| * If no matching value - do nothing |
| * If 1 matching value - insert |
| */ |
| static void _bcmcli_extend_name(bcmcli_session_extras *mon_session, const char *partial_value, |
| char *insert_str, uint32_t insert_size) |
| { |
| char longest_match[BCMCLI_MAX_SEARCH_SUBSTR_LENGTH]=""; |
| int first_match; |
| int nmatch = _bcmcli_longest_match(mon_session, partial_value, longest_match, |
| sizeof(longest_match), &first_match); |
| |
| if (!nmatch) |
| return; |
| if (!partial_value || strcmp(partial_value, longest_match)) |
| _bcmcli_insert(partial_value, longest_match, (nmatch == 1) ? "=" : "", insert_str, insert_size); |
| else |
| _bcmcli_help_populated_cmd(mon_session, mon_session->curcmd, partial_value, BCMOS_TRUE); |
| } |
| |
| static int _bcmcli_extend_parms( bcmcli_session_extras *mon_session, bcmcli_name_value *pairs, |
| int npairs, bcmos_bool last_is_space, char *insert_str, uint32_t insert_size) |
| { |
| bcmos_errno rc; |
| int last = 0; |
| bcmcli_cmd_parm *help_parm = NULL; |
| int i; |
| |
| rc = _bcmcli_populate_parms(mon_session, mon_session->curcmd, pairs, npairs, BCMOS_TRUE, &last); |
| if (!rc) |
| { |
| /* So far so good */ |
| /* If there is unset mandatory parameter - insert its name. |
| * Otherwise, display list of unset parameters |
| */ |
| /* Find mandatory parameter that is still unassigned */ |
| for (i = 0; i < mon_session->num_parms; i++) |
| { |
| uint32_t flags = mon_session->cmd_parms[i].flags; |
| if (!(flags & (BCMCLI_PARM_FLAG_OPTIONAL | BCMCLI_PARM_FLAG_DEFVAL | BCMCLI_PARM_FLAG_ASSIGNED))) |
| { |
| help_parm = &mon_session->cmd_parms[i]; |
| break; |
| } |
| } |
| if (help_parm) |
| { |
| if (!last_is_space) |
| bcmcli_strncpy(insert_str, " ", insert_size); |
| bcmcli_strncat(insert_str, help_parm->name, insert_size); |
| bcmcli_strncat(insert_str, "=", insert_size); |
| } |
| else if (last < mon_session->num_parms) |
| _bcmcli_help_populated_cmd(mon_session, mon_session->curcmd, NULL, BCMOS_TRUE); |
| } |
| else |
| { |
| /* Parsing failed. See what stopped at */ |
| if (last < mon_session->num_parms) |
| { |
| bcmcli_name_value *last_pair; |
| |
| last_pair = &pairs[last]; |
| if (last_pair->name) |
| { |
| /* Try to identify by name */ |
| help_parm = _bcmcli_find_named_parm(mon_session, last_pair->name ? last_pair->name : last_pair->value); |
| } |
| if (help_parm) |
| { |
| /* Looking for values */ |
| _bcmcli_extend_value(mon_session, help_parm, last_pair->value, insert_str, insert_size); |
| } |
| else |
| { |
| /* Looking for partial name */ |
| _bcmcli_extend_name(mon_session, last_pair->name ? last_pair->name : last_pair->value, |
| insert_str, insert_size); |
| } |
| } |
| } |
| _bcmcli_free_session_value_status(mon_session); |
| |
| return BCM_ERR_OK; |
| } |
| |
| /* Identify token in the given directory */ |
| static bcmcli_entry *_bcmcli_search_token1( bcmcli_entry *p_dir, const char **p_name, int name_len ) |
| { |
| bcmcli_entry *p_token = NULL; |
| const char *name = *p_name; |
| bcmcli_token_type type=_bcmcli_analyze_token(name); |
| |
| /* Name can be qualified */ |
| if (type == BCMCLI_TOKEN_VALUE && !strncmp(name, BCMCLI_UP_STR, name_len)) |
| type = BCMCLI_TOKEN_UP; |
| |
| switch(type) |
| { |
| case BCMCLI_TOKEN_ROOT: |
| p_token = _bcmcli_root_dir; |
| *p_name = name + strlen(BCMCLI_ROOT_STR); |
| break; |
| case BCMCLI_TOKEN_UP: |
| if (p_dir->parent) |
| p_token = p_dir->parent; |
| else |
| p_token = p_dir; |
| *p_name = name + strlen(BCMCLI_UP_STR) + 1; |
| break; |
| case BCMCLI_TOKEN_NAME: |
| case BCMCLI_TOKEN_VALUE: |
| /* Check alias */ |
| p_token = p_dir->u.dir.first; |
| while ( p_token ) |
| { |
| if (p_token->alias && |
| (name_len == p_token->alias_len) && |
| !_bcmcli_stricmp(p_token->alias, name, p_token->alias_len)) |
| break; |
| p_token = p_token->next; |
| } |
| if (!p_token) |
| { |
| bcmcli_entry *partial_match = NULL; |
| /* Check name */ |
| p_token = p_dir->u.dir.first; |
| while( p_token ) |
| { |
| if (!_bcmcli_stricmp(p_token->name, name, name_len)) |
| { |
| if (name_len == strlen(p_token->name)) |
| break; |
| if (!partial_match) |
| partial_match = p_token; |
| } |
| p_token = p_token->next; |
| } |
| if (!p_token) |
| p_token = partial_match; |
| } |
| *p_name = name + name_len + 1; |
| break; |
| default: |
| break; |
| } |
| |
| return p_token; |
| } |
| |
| |
| /* Search a token by name in the current directory. |
| * The name can be qualified (contain path) |
| */ |
| static bcmcli_entry *_bcmcli_search_token( bcmcli_entry *p_dir, const char *name ) |
| { |
| bcmcli_entry *p_token; |
| const char *name0 = name; |
| const char *p_slash; |
| |
| if (!name[0]) |
| return p_dir; |
| |
| /* Check if name is qualified */ |
| do |
| { |
| p_slash = strchr(name, '/'); |
| if (p_slash) |
| { |
| if (p_slash == name0) |
| { |
| p_dir = p_token = _bcmcli_root_dir; |
| name = p_slash + 1; |
| } |
| else |
| { |
| p_token = _bcmcli_search_token1(p_dir, &name, p_slash - name); |
| if (p_token && (p_token->sel == BCMCLI_ENTRY_DIR)) |
| p_dir = p_token; |
| } |
| } |
| else |
| { |
| p_token = _bcmcli_search_token1(p_dir, &name, strlen(name)); |
| } |
| } while (p_slash && p_token && *name); |
| |
| return p_token; |
| } |
| |
| |
| |
| /* Display help for each entry in the current directory */ |
| static void _bcmcli_help_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir) |
| { |
| bcmcli_session *help_session = _bcmcli_help_session_open(mon_session->session); |
| bcmcli_entry *p_token; |
| char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH]; |
| |
| _bcmcli_qualified_name(p_dir, buffer, sizeof(buffer)); |
| bcmcli_session_print(help_session, "Directory %s/ - %s\n", buffer, p_dir->help); |
| bcmcli_session_print(help_session, "Commands:\n"); |
| |
| p_token = p_dir->u.dir.first; |
| while ( p_token ) |
| { |
| if (bcmcli_session_access_right(help_session) >= p_token->access_right) |
| { |
| if (p_token->sel == BCMCLI_ENTRY_DIR) |
| bcmcli_session_print(help_session, "\t%s/: %s directory\n", p_token->name, p_token->help ); |
| else |
| { |
| char *peol = strchr(p_token->help, '\n'); |
| int help_len = peol ? peol - p_token->help : (int)strlen(p_token->help); |
| bcmcli_session_print(help_session, "\t%s(%d parms): %.*s\n", |
| p_token->name, p_token->u.cmd.num_parms, help_len, p_token->help ); |
| } |
| } |
| p_token = p_token->next; |
| } |
| bcmcli_session_print(help_session, "Type ? <name> for command help, \"/\"-root, \"..\"-upper\n" ); |
| _bcmcli_help_session_print_and_close(mon_session->session, help_session); |
| } |
| |
| |
| /* Display help a token */ |
| static void _bcmcli_help_populated_cmd(bcmcli_session_extras *mon_session, bcmcli_entry *p_token, |
| const char *partial_match, bcmos_bool suppress_assigned) |
| { |
| char tmp[80]; |
| char bra, ket; |
| uint16_t i; |
| |
| if (suppress_assigned) |
| bcmcli_session_print(mon_session->session, "\n"); |
| for ( i=0; i<mon_session->num_parms; i++ ) |
| { |
| bcmcli_cmd_parm *cur_parm = &mon_session->cmd_parms[i]; |
| if (suppress_assigned && (cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| continue; |
| if (partial_match && memcmp(partial_match, cur_parm->name, strlen(partial_match))) |
| continue; |
| |
| if ((cur_parm->flags & BCMCLI_PARM_FLAG_OPTIONAL)) |
| { |
| bra = '['; |
| ket=']'; |
| } |
| else |
| { |
| bra = '<'; |
| ket='>'; |
| } |
| bcmcli_session_print(mon_session->session, "\t%c%s(%s)", bra, cur_parm->name, _bcmcli_get_type_name(cur_parm) ); |
| if (cur_parm->max_array_size || cur_parm->type == BCMCLI_PARM_BUFFER) |
| { |
| uint32_t num_entries = (cur_parm->type == BCMCLI_PARM_BUFFER) ? cur_parm->value.buffer.len : cur_parm->max_array_size; |
| bcmcli_session_print(mon_session->session, "[%u]", num_entries); |
| } |
| if (cur_parm->type == BCMCLI_PARM_ENUM || cur_parm->type == BCMCLI_PARM_ENUM_MASK) |
| { |
| bcmcli_enum_val *values=cur_parm->enum_table; |
| bcmcli_session_print(mon_session->session, " {"); |
| while (values->name) |
| { |
| if (values!=cur_parm->enum_table) |
| bcmcli_session_print(mon_session->session, ", "); |
| bcmcli_session_print(mon_session->session, "%s", values->name); |
| ++values; |
| } |
| bcmcli_session_print(mon_session->session, "}"); |
| } |
| if ((cur_parm->flags & BCMCLI_PARM_FLAG_DEFVAL)) |
| { |
| bcmcli_session_print(mon_session->session, "="); |
| cur_parm->format_cb(cur_parm, cur_parm->value, tmp, sizeof(tmp)); |
| bcmcli_session_print(mon_session->session, "%s", tmp); |
| } |
| if ((cur_parm->flags & BCMCLI_PARM_FLAG_RANGE)) |
| { |
| bcmcli_parm_value low_val = { .number = cur_parm->low_val }; |
| bcmcli_parm_value hi_val = { .number = cur_parm->hi_val }; |
| |
| bcmcli_session_print(mon_session->session, " ("); |
| cur_parm->format_cb(cur_parm, low_val, tmp, sizeof(tmp)); |
| bcmcli_session_print(mon_session->session, "%s..", tmp); |
| cur_parm->format_cb(cur_parm, hi_val, tmp, sizeof(tmp)); |
| bcmcli_session_print(mon_session->session, "%s)", tmp); |
| } |
| bcmcli_session_print(mon_session->session, "%c ", ket); |
| bcmcli_session_print(mon_session->session, "- %s\n", cur_parm->description); |
| } |
| |
| /* Print extra help if command has unresolved selector */ |
| if (mon_session->num_parms && |
| (mon_session->cmd_parms[mon_session->num_parms-1].flags & BCMCLI_PARM_FLAG_SELECTOR) && |
| !(mon_session->cmd_parms[mon_session->num_parms-1].flags & BCMCLI_PARM_FLAG_ASSIGNED)) |
| { |
| const char *sel_name = mon_session->cmd_parms[mon_session->num_parms-1].name; |
| bcmcli_session_print(mon_session->session, "Add %s=%s_value to see %s-specific parameters\n", |
| sel_name, sel_name, sel_name); |
| } |
| bcmcli_session_print(mon_session->session, "\n"); |
| } |
| |
| |
| /* Display help a token */ |
| static void _bcmcli_help_entry(bcmcli_session_extras *mon_session, bcmcli_entry *p_token, |
| bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print) |
| { |
| char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH]; |
| |
| if (p_token->sel == BCMCLI_ENTRY_DIR) |
| { |
| _bcmcli_help_dir(mon_session, p_token); |
| return; |
| } |
| |
| /* Populate parameter table */ |
| _bcmcli_populate_parms(mon_session, p_token, pairs, npairs, suppress_err_print, NULL); |
| |
| _bcmcli_qualified_name(p_token, buffer, sizeof(buffer)); |
| bcmcli_session_print(mon_session->session, "%s: \t%s\n", buffer, p_token->help ); |
| if (p_token->u.cmd.num_parms) |
| bcmcli_session_print(mon_session->session, "Parameters:\n"); |
| _bcmcli_help_populated_cmd(mon_session, p_token, NULL, BCMOS_FALSE); |
| _bcmcli_free_session_value_status(mon_session); |
| } |
| |
| |
| /* Choose unique alias for <name> in <p_dir> */ |
| /* Currently only single-character aliases are supported */ |
| static void __bcmcli_chooseAlias(bcmcli_entry *p_dir, bcmcli_entry *p_new_token, int from) |
| { |
| bcmcli_entry *p_token; |
| int i; |
| char c; |
| |
| _bcmcli_strlwr( p_new_token->name ); |
| i = from; |
| while ( p_new_token->name[i] ) |
| { |
| c = p_new_token->name[i]; |
| p_token = p_dir->u.dir.first; |
| |
| while ( p_token ) |
| { |
| if (p_token->alias && |
| (tolower( *p_token->alias ) == c) ) |
| break; |
| if (strlen(p_token->name)<=2 && tolower(p_token->name[0])==c) |
| break; |
| p_token = p_token->next; |
| } |
| if (p_token) |
| ++i; |
| else |
| { |
| p_new_token->name[i] = toupper( c ); |
| p_new_token->alias = &p_new_token->name[i]; |
| p_new_token->alias_len = 1; |
| break; |
| } |
| } |
| } |
| |
| /* isupper wrapper */ |
| static inline int _bcmcli_isupper(char c) |
| { |
| return isupper((int)c); |
| } |
| |
| static void _bcmcli_choose_alias(bcmcli_entry *p_dir, bcmcli_entry *p_new_token) |
| { |
| int i=0; |
| p_new_token->alias_len = 0; |
| p_new_token->alias = NULL; |
| /* Don't try to alias something short */ |
| if (strlen(p_new_token->name) < BCMCLI_MIN_NAME_LENGTH_FOR_ALIAS) |
| return; |
| /* Try pre-set alias 1st */ |
| while ( p_new_token->name[i] ) |
| { |
| if (_bcmcli_isupper(p_new_token->name[i])) |
| break; |
| i++; |
| } |
| if (p_new_token->name[i]) |
| __bcmcli_chooseAlias(p_dir, p_new_token, i); |
| if (p_new_token->alias != &p_new_token->name[i]) |
| __bcmcli_chooseAlias(p_dir, p_new_token, 0); |
| } |
| |
| |
| /* Convert string s to lower case. Return pointer to s */ |
| static char * _bcmcli_strlwr( char *s ) |
| { |
| char *s0=s; |
| |
| while ( *s ) |
| { |
| *s = tolower( *s ); |
| ++s; |
| } |
| |
| return s0; |
| } |
| |
| |
| /* Compare strings case incensitive */ |
| static int _bcmcli_stricmp(const char *s1, const char *s2, int len) |
| { |
| int i; |
| |
| for ( i=0; (i<len || len<0); i++ ) |
| { |
| if (tolower( s1[i]) != tolower( s2[i] )) |
| return 1; |
| if (!s1[i]) |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static const char *_bcmcli_get_type_name(const bcmcli_cmd_parm *parm) |
| { |
| bcmcli_parm_type type = parm->type; |
| static const char *type_name[] = { |
| [BCMCLI_PARM_DECIMAL] = "decimal", |
| [BCMCLI_PARM_DECIMAL64] = "decimal64", |
| [BCMCLI_PARM_UDECIMAL] = "udecimal", |
| [BCMCLI_PARM_UDECIMAL64] = "udecimal64", |
| [BCMCLI_PARM_HEX] = "hex", |
| [BCMCLI_PARM_HEX64] = "hex64", |
| [BCMCLI_PARM_NUMBER] = "number", |
| [BCMCLI_PARM_NUMBER64] = "number64", |
| [BCMCLI_PARM_UNUMBER] = "unumber", |
| [BCMCLI_PARM_UNUMBER64] = "unumber64", |
| [BCMCLI_PARM_FLOAT] = "float", |
| [BCMCLI_PARM_DOUBLE] = "double", |
| [BCMCLI_PARM_ENUM] = "enum", |
| [BCMCLI_PARM_ENUM_MASK] = "enum_mask", |
| [BCMCLI_PARM_STRING] = "string", |
| [BCMCLI_PARM_IP] = "IP", |
| [BCMCLI_PARM_IPV6] = "IPv6", |
| [BCMCLI_PARM_MAC] = "MAC", |
| [BCMCLI_PARM_BUFFER] = "buffer", |
| [BCMCLI_PARM_USERDEF] = "userdef", |
| }; |
| static const char *undefined = "undefined"; |
| static const char *selector = "selector"; |
| if (type > BCMCLI_PARM_USERDEF || !type_name[type]) |
| return undefined; |
| if (type == BCMCLI_PARM_ENUM && (parm->flags & BCMCLI_PARM_FLAG_SELECTOR)) |
| return selector; |
| return type_name[type]; |
| } |
| |
| /* Assign default callbacks */ |
| static void _bcmcli_assign_callbacks(bcmcli_cmd_parm *parm) |
| { |
| if (parm->type == BCMCLI_PARM_ENUM) |
| { |
| parm->scan_cb = _bcmcli_enum_scan_cb; |
| parm->format_cb = _bcmcli_enum_format_cb; |
| } |
| else if (parm->type == BCMCLI_PARM_ENUM_MASK) |
| { |
| parm->scan_cb = _bcmcli_enum_mask_scan_cb; |
| parm->format_cb = _bcmcli_enum_mask_format_cb; |
| } |
| else if (parm->type == BCMCLI_PARM_BUFFER) |
| { |
| if (!parm->scan_cb) |
| parm->scan_cb = _bcmcli_buffer_scan_cb; |
| if (!parm->format_cb) |
| parm->format_cb = _bcmcli_dft_format_cb; |
| } |
| else |
| { |
| if (!parm->scan_cb) |
| parm->scan_cb = _bcmcli_dft_scan_cb; |
| if (!parm->format_cb) |
| parm->format_cb = _bcmcli_dft_format_cb; |
| } |
| } |
| |
| |
| /* Convert hex-string to binary data. |
| * Returns: converted length >=0 or error < 0 |
| */ |
| static int _bcmcli_strhex(const char *src, uint8_t *dst, uint16_t dst_len) |
| { |
| uint16_t src_len = (uint16_t)strlen( src ); |
| uint16_t i = src_len, j, shift = 0; |
| |
| if ( !dst || !dst_len || (src_len > 2*dst_len) || (src_len%2) ) |
| { |
| return BCM_ERR_PARM; |
| } |
| |
| /* Calculate hex buffer length and fill it up from right-to-left |
| * in order to start the process from LS nibble |
| */ |
| dst_len = src_len / 2; |
| memset(dst, 0, dst_len); |
| j = dst_len-1; |
| do |
| { |
| int c = src[--i]; |
| |
| if ( (c>='0') && (c<='9') ) |
| { |
| c = c - '0'; |
| } |
| else if ( (c>='a') && (c<='f') ) |
| { |
| c = 0xA + c - 'a'; |
| } |
| else if ( (c>='A') && (c<='F') ) |
| { |
| c = 0xA + c - 'A'; |
| } |
| else |
| { |
| return BCM_ERR_PARM; |
| } |
| |
| dst[j] |= (uint8_t)(c<<shift); /* shift can have 1 of 2 values: 0 and 4 */ |
| |
| j -= shift>>2; /* move to the next byte if we've just filled the ms nibble */ |
| shift ^= 4; /* alternate nibbles */ |
| |
| } while ( i ); |
| |
| return dst_len; |
| } |
| |
| /* Default function for string->value conversion. |
| * Returns 0 if OK |
| */ |
| static bcmos_errno _bcmcli_dft_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val) |
| { |
| char *p_end = NULL; |
| int n; |
| |
| if (parm->type == BCMCLI_PARM_UDECIMAL || |
| parm->type == BCMCLI_PARM_UDECIMAL64 || |
| parm->type == BCMCLI_PARM_UNUMBER || |
| parm->type == BCMCLI_PARM_UNUMBER64) |
| { |
| /* strtoul returns OK even when parsing a negative number */ |
| if (string_val[0] == '-') |
| { |
| return BCM_ERR_PARM; |
| } |
| } |
| |
| switch(parm->type) |
| { |
| case BCMCLI_PARM_DECIMAL: |
| value->number = strtol(string_val, &p_end, 10); |
| break; |
| case BCMCLI_PARM_UDECIMAL: |
| value->unumber = strtoul(string_val, &p_end, 10); |
| break; |
| case BCMCLI_PARM_DECIMAL64: |
| value->number64 = strtoll(string_val, &p_end, 10); |
| break; |
| case BCMCLI_PARM_UDECIMAL64: |
| value->unumber64 = strtoull(string_val, &p_end, 10); |
| break; |
| case BCMCLI_PARM_HEX: |
| value->unumber = strtoul(string_val, &p_end, 16); |
| break; |
| case BCMCLI_PARM_HEX64: |
| value->unumber64 = strtoull(string_val, &p_end, 16); |
| break; |
| case BCMCLI_PARM_NUMBER: |
| value->number = strtol(string_val, &p_end, 0); |
| break; |
| case BCMCLI_PARM_UNUMBER: |
| value->unumber = strtoul(string_val, &p_end, 0); |
| break; |
| case BCMCLI_PARM_NUMBER64: |
| value->number64 = strtoll(string_val, &p_end, 0); |
| break; |
| case BCMCLI_PARM_UNUMBER64: |
| value->unumber64 = strtoull(string_val, &p_end, 0); |
| break; |
| case BCMCLI_PARM_FLOAT: |
| case BCMCLI_PARM_DOUBLE: |
| value->d = strtod(string_val, &p_end); |
| break; |
| case BCMCLI_PARM_MAC: |
| { |
| unsigned m0, m1, m2, m3, m4, m5; |
| n = sscanf(string_val, "%02x:%02x:%02x:%02x:%02x:%02x", |
| &m0, &m1, &m2, &m3, &m4, &m5); |
| if (n != 6) |
| { |
| n = sscanf(string_val, "%02x%02x%02x%02x%02x%02x", |
| &m0, &m1, &m2, &m3, &m4, &m5); |
| } |
| if (n != 6) |
| return BCM_ERR_PARM; |
| if (m0 > 255 || m1 > 255 || m2 > 255 || m3 > 255 || m4 > 255 || m5 > 255) |
| return BCM_ERR_PARM; |
| value->mac.u8[0] = m0; |
| value->mac.u8[1] = m1; |
| value->mac.u8[2] = m2; |
| value->mac.u8[3] = m3; |
| value->mac.u8[4] = m4; |
| value->mac.u8[5] = m5; |
| break; |
| } |
| case BCMCLI_PARM_IP: |
| { |
| int n1, n2, n3, n4; |
| n = sscanf(string_val, "%d.%d.%d.%d", &n1, &n2, &n3, &n4); |
| if (n != 4) |
| return BCM_ERR_PARM; |
| if ((unsigned)n1 > 255 || (unsigned)n2 > 255 || (unsigned)n3 > 255 || (unsigned)n4 > 255) |
| return BCM_ERR_PARM; |
| value->unumber = (n1 << 24) | (n2 << 16) | (n3 << 8) | n4; |
| break; |
| } |
| |
| default: |
| return BCM_ERR_PARM; |
| } |
| if (p_end && *p_end) |
| return BCM_ERR_PARM; |
| return BCM_ERR_OK; |
| } |
| |
| static void _bcmcli_dft_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size) |
| { |
| switch(parm->type) |
| { |
| case BCMCLI_PARM_DECIMAL: |
| snprintf(buffer, size, "%ld", value.number); |
| break; |
| case BCMCLI_PARM_UDECIMAL: |
| snprintf(buffer, size, "%lu", value.unumber); |
| break; |
| case BCMCLI_PARM_DECIMAL64: |
| snprintf(buffer, size, "%lld", value.number64); |
| break; |
| case BCMCLI_PARM_UDECIMAL64: |
| snprintf(buffer, size, "%llu", value.unumber64); |
| break; |
| case BCMCLI_PARM_HEX: |
| snprintf(buffer, size, "0x%lx", value.unumber); |
| break; |
| case BCMCLI_PARM_HEX64: |
| snprintf(buffer, size, "0x%llx", value.unumber64); |
| break; |
| case BCMCLI_PARM_NUMBER: |
| snprintf(buffer, size, "%ld", value.number); |
| break; |
| case BCMCLI_PARM_NUMBER64: |
| snprintf(buffer, size, "%lld", value.number64); |
| break; |
| case BCMCLI_PARM_UNUMBER: |
| snprintf(buffer, size, "%lu", value.unumber); |
| break; |
| case BCMCLI_PARM_UNUMBER64: |
| snprintf(buffer, size, "%llu", value.unumber64); |
| break; |
| case BCMCLI_PARM_FLOAT: |
| case BCMCLI_PARM_DOUBLE: |
| snprintf(buffer, size, "%f", value.d); |
| break; |
| case BCMCLI_PARM_STRING: |
| snprintf(buffer, size, "%s", value.string); |
| break; |
| case BCMCLI_PARM_MAC: |
| snprintf(buffer, size, "%02x:%02x:%02x:%02x:%02x:%02x", |
| parm->value.mac.u8[0], parm->value.mac.u8[1], parm->value.mac.u8[2], |
| parm->value.mac.u8[3], parm->value.mac.u8[4], parm->value.mac.u8[5]); |
| break; |
| case BCMCLI_PARM_IP: |
| snprintf(buffer, size, "%d.%d.%d.%d", |
| (int)((parm->value.unumber >> 24) & 0xff), (int)((parm->value.unumber >> 16) & 0xff), |
| (int)((parm->value.unumber >> 8) & 0xff), (int)(parm->value.unumber & 0xff)); |
| break; |
| |
| default: |
| bcmcli_strncpy(buffer, "*unknown*", size); |
| } |
| } |
| |
| static bcmos_errno _bcmcli_enum_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val) |
| { |
| bcmcli_enum_val *values=parm->enum_table; |
| while (values->name) |
| { |
| if (!_bcmcli_stricmp(values->name, string_val, -1)) |
| { |
| value->enum_val = values->val; |
| return BCM_ERR_OK; |
| } |
| ++values; |
| } |
| return BCM_ERR_PARM; |
| } |
| |
| static void _bcmcli_enum_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size) |
| { |
| bcmcli_enum_val *values=parm->enum_table; |
| while (values->name) |
| { |
| if (values->val == value.enum_val) |
| break; |
| ++values; |
| } |
| if (values->name) |
| strncpy(buffer, values->name, size); |
| else |
| strncpy(buffer, "*invalid*", size); |
| } |
| |
| static bcmos_errno _bcmcli_enum_mask_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val) |
| { |
| bcmcli_parm_value val1; |
| char *del; |
| bcmos_errno err; |
| |
| value->number = 0; |
| |
| /* string_val is a combination of enum values separated by BCMCLI_ENUM_MASK_DEL_STR */ |
| del = strchr(string_val, BCMCLI_ENUM_MASK_DEL_CHAR); |
| while (del) |
| { |
| char single_val[64]; |
| if (del - string_val >= sizeof(single_val)) |
| return BCM_ERR_OVERFLOW; |
| memcpy(single_val, string_val, del - string_val); |
| single_val[del - string_val] = 0; |
| err = _bcmcli_enum_scan_cb(parm, &val1, single_val); |
| if (err) |
| return err; |
| value->enum_val |= val1.enum_val; |
| string_val = del+1; |
| del = strchr(string_val, BCMCLI_ENUM_MASK_DEL_CHAR); |
| } |
| err = _bcmcli_enum_scan_cb(parm, &val1, string_val); |
| if (err) |
| return err; |
| value->number |= val1.enum_val; |
| return BCM_ERR_OK; |
| } |
| |
| static void _bcmcli_enum_mask_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size) |
| { |
| bcmcli_enum_val *values=parm->enum_table; |
| const char *none = NULL; |
| *buffer = 0; |
| while (values->name) |
| { |
| if (values->val == 0) |
| { |
| none = values->name; |
| } |
| if ((values->val & value.enum_val) != 0) |
| { |
| if (*buffer) |
| strncat(buffer, BCMCLI_ENUM_MASK_DEL_STR, size - strlen(buffer)); |
| strncat(buffer, values->name, size - strlen(buffer)); |
| } |
| ++values; |
| } |
| if (! *buffer) |
| strncpy(buffer, NULL != none ? none : "0", size); |
| } |
| |
| static bcmos_errno _bcmcli_buffer_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val) |
| { |
| int n; |
| |
| if (!value->buffer.start) |
| return BCM_ERR_PARM; |
| value->buffer.curr = value->buffer.start; |
| if (strcmp(string_val, "-") == 0) |
| { |
| return BCM_ERR_OK; |
| } |
| n = _bcmcli_strhex(string_val, value->buffer.start, value->buffer.len); |
| if (n < 0) |
| return n; |
| bcmolt_buf_skip(&value->buffer, n); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static const char *_bcmcli_qualified_name(bcmcli_entry *token, char *buffer, int size ) |
| { |
| bcmcli_entry *parent = token->parent; |
| char qual_name[BCMCLI_MAX_QUAL_NAME_LENGTH]; |
| *buffer=0; |
| while (parent) |
| { |
| bcmcli_strncpy(qual_name, parent->name, sizeof(qual_name)); |
| if (parent->parent) |
| bcmcli_strncat(qual_name, "/", sizeof(qual_name)); |
| bcmcli_strncat(qual_name, buffer, sizeof(qual_name)); |
| bcmcli_strncpy(buffer, qual_name, size); |
| parent = parent->parent; |
| } |
| size -= strlen(buffer); |
| bcmcli_strncat(buffer, token->name, size); |
| return buffer; |
| } |
| |
| /* |
| * Exports |
| */ |
| EXPORT_SYMBOL(bcmcli_dir_add); |
| EXPORT_SYMBOL(bcmcli_dir_find); |
| EXPORT_SYMBOL(bcmcli_token_name); |
| EXPORT_SYMBOL(bcmcli_cmd_add); |
| EXPORT_SYMBOL(bcmcli_session_open); |
| EXPORT_SYMBOL(bcmcli_session_close); |
| EXPORT_SYMBOL(bcmcli_parse); |
| EXPORT_SYMBOL(bcmcli_stop); |
| EXPORT_SYMBOL(bcmcli_is_stopped); |
| EXPORT_SYMBOL(bcmcli_dir_get); |
| EXPORT_SYMBOL(bcmcli_dir_set); |
| EXPORT_SYMBOL(bcmcli_parm_number); |
| EXPORT_SYMBOL(bcmcli_parm_is_set); |
| EXPORT_SYMBOL(bcmcli_enum_parm_stringval); |
| EXPORT_SYMBOL(bcmcli_token_destroy); |
| EXPORT_SYMBOL(bcmcli_enum_bool_table); |