BAL and Maple Release 2.2
Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli.c b/bcm68620_release/release/host_reference/cli/bcmcli.c
new file mode 100644
index 0000000..9190e5d
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli.c
@@ -0,0 +1,2835 @@
+/*
+<: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);