blob: 9190e5d353bb89659b9c021e987de660faf5a630 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30
31/*******************************************************************
32 * bcmcli.c
33 *
34 * CLI engine
35 *
36 *******************************************************************/
37#include <bcmos_system.h>
38#define BCMCLI_INTERNAL
39#include <bcmcli.h>
40#include <bcmos_types.h>
41
42#define BCMCLI_INBUF_LEN 2048
43#define BCMCLI_MAX_QUAL_NAME_LENGTH 256
44#define BCMCLI_MAX_PARMS 128
45#define BCMCLI_UP_STR ".."
46#define BCMCLI_ROOT_STR "/"
47#define BCMCLI_COMMENT_CHAR '#'
48#define BCMCLI_HELP_CHAR '?'
49#define BCMCLI_ARRAY_DELIM_CHAR ','
50#define BCMCLI_ROOT_HELP "root directory"
51#define BCMCLI_MAX_PARM_VAL_LEN 256
52#define BCMCLI_ENUM_MASK_DEL_CHAR '+'
53#define BCMCLI_HELP_BUFFER_SIZE 16384
54
55#define BCMCLI_EQUAL_CHAR '='
56
57
58typedef enum { BCMCLI_ENTRY_DIR, BCMCLI_ENTRY_CMD } bcmcli_entry_selector;
59
60/* External table - boolean values */
61bcmcli_enum_val bcmcli_enum_bool_table[] = {
62 { .name="true", .val = 1 },
63 { .name="yes", .val = 1 },
64 { .name="on", .val = 1 },
65 { .name="false", .val = 0 },
66 { .name="no", .val = 0 },
67 { .name="off", .val = 0 },
68 BCMCLI_ENUM_LAST
69};
70
71/* Monitor token structure */
72struct bcmcli_entry
73{
74 struct bcmcli_entry *next;
75 char *name; /* Command/directory name */
76 char *help; /* Command/directory help */
77 bcmcli_entry_selector sel; /* Entry selector */
78 char *alias; /* Alias */
79 uint16_t alias_len; /* Alias length */
80 struct bcmcli_entry *parent; /* Parent directory */
81 bcmcli_access_right access_right;
82
83 union {
84 struct
85 {
86 struct bcmcli_entry *first; /* First entry in directory */
87 bcmcli_dir_extra_parm extras; /* Optional extras */
88 } dir;
89 struct
90 {
91 bcmcli_cmd_cb cmd_cb; /* Command callback */
92 bcmcli_cmd_parm *parms; /* Command parameters */
93 bcmcli_cmd_extra_parm extras; /* Optional extras */
94 uint16_t num_parms;
95 } cmd;
96 } u;
97};
98
99
100/* Token types */
101typedef enum
102{
103 BCMCLI_TOKEN_EMPTY,
104 BCMCLI_TOKEN_UP,
105 BCMCLI_TOKEN_ROOT,
106 BCMCLI_TOKEN_BREAK,
107 BCMCLI_TOKEN_HELP,
108 BCMCLI_TOKEN_NAME,
109 BCMCLI_TOKEN_VALUE,
110} bcmcli_token_type;
111
112/* Parameter value set descriptor */
113typedef union bcmcli_parm_value_status
114{
115 bcmos_bool value_set;
116 bcmos_bool *values_set;
117} bcmcli_parm_value_status;
118
119/* CLI session data */
120typedef struct bcmcli_session_data
121{
122 bcmcli_entry *curdir;
123 bcmcli_entry *curcmd;
124 bcmcli_cmd_parm cmd_parms[BCMCLI_MAX_PARMS];
125 bcmcli_parm_value_status value_status[BCMCLI_MAX_PARMS];
126 bcmcli_session *session;
127 uint16_t num_parms;
128 char *p_inbuf;
129 int stop_monitor;
130 char inbuf[BCMCLI_INBUF_LEN];
131} bcmcli_session_extras;
132
133/* Name, value pairs */
134typedef struct bcmcli_name_value
135{
136 bcmcli_token_type type;
137 const char *name;
138 const char *value;
139} bcmcli_name_value;
140
141static bcmcli_entry *_bcmcli_root_dir;
142static bcmcli_session_extras *_bcmcli_root_session;
143static bcmcli_log_mode _bcmcli_log_mode;
144static bcmcli_session *_bcmcli_log_session;
145
146#define BCMCLI_MIN_NAME_LENGTH_FOR_ALIAS 3
147#define BCMCLI_ROOT_NAME "/"
148
149/* Internal functions */
150static void _bcmcli_alloc_root(const bcmcli_session_parm *parm);
151static void _bcmcli_display_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir );
152static bcmcli_token_type _bcmcli_get_word(bcmcli_session_extras *session, char **inbuf, char **p_word);
153static bcmcli_token_type _bcmcli_analyze_token( const char *name );
154static int _bcmcli_parse_parms( bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
155 bcmcli_name_value *pairs, int npairs);
156static int _bcmcli_extend_parms( bcmcli_session_extras *mon_session, bcmcli_name_value *pairs,
157 int npairs, bcmos_bool last_is_space, char *insert_str, uint32_t insert_size);
158static bcmcli_entry *_bcmcli_search_token( bcmcli_entry *p_dir, const char *name );
159static void _bcmcli_help_dir( bcmcli_session_extras *mon_session, bcmcli_entry *p_dir );
160static void _bcmcli_help_entry(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
161 bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print);
162static void _bcmcli_help_populated_cmd(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
163 const char *partial_match, bcmos_bool suppress_assigned);
164static void _bcmcli_choose_alias( bcmcli_entry *p_dir, bcmcli_entry *p_new_token );
165static bcmcli_cmd_parm *_bcmcli_find_named_parm(bcmcli_session_extras *mon_session, const char *name);
166static char *_bcmcli_strlwr( char *s );
167static int _bcmcli_stricmp( const char *s1, const char *s2, int len );
168static bcmos_errno _bcmcli_dft_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
169static const char *_bcmcli_get_type_name(const bcmcli_cmd_parm *parm);
170static void _bcmcli_dft_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size);
171static bcmos_errno _bcmcli_enum_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
172static void _bcmcli_enum_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size);
173static bcmos_errno _bcmcli_enum_mask_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
174static void _bcmcli_enum_mask_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size);
175static bcmos_errno _bcmcli_buffer_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
176static const char *_bcmcli_qualified_name( bcmcli_entry *token, char *buffer, int size);
177static bcmos_errno _bcmcli_split(bcmcli_session_extras *mon_session, bcmcli_name_value **pairs, int *npairs);
178static void _bcmcli_assign_callbacks(bcmcli_cmd_parm *parm);
179static void _bcmcli_log_cmd(const char *cmd);
180static void _bcmcli_log_rc(bcmos_errno rc);
181static void _bcmcli_free_session_value_status(bcmcli_session_extras *mon_session);
182
183static inline bcmcli_session_extras *_bcmcli_session_data(bcmcli_session *session)
184{
185 if (!session)
186 return _bcmcli_root_session;
187 return bcmcli_session_data(session);
188}
189
190/** Add subdirectory to the parent directory
191 *
192 * \param[in] parent Parent directory handle. NULL=root
193 * \param[in] name Directory name
194 * \param[in] help Help string
195 * \param[in] access_right Access rights
196 * \param[in] extras Optional directory descriptor. Mustn't be allocated on the stack.
197 * \return new directory handle or NULL in case of failure
198 */
199bcmcli_entry *bcmcli_dir_add(bcmcli_entry *parent, const char *name,
200 const char *help, bcmcli_access_right access_right,
201 const bcmcli_dir_extra_parm *extras)
202{
203 bcmcli_entry *p_dir;
204 bcmcli_entry **p_e;
205
206 assert(name);
207 assert(help);
208 if (!name || !help)
209 return NULL;
210
211 if (!_bcmcli_root_dir)
212 {
213 _bcmcli_alloc_root(NULL);
214 if (!_bcmcli_root_dir)
215 return NULL;
216 }
217
218 if (!parent)
219 parent = _bcmcli_root_dir;
220
221 p_dir=(bcmcli_entry *)bcmos_calloc( sizeof(bcmcli_entry) + strlen(name) + strlen(help) + 2 );
222 if ( !p_dir )
223 return NULL;
224
225 p_dir->name = (char *)(p_dir + 1);
226 strcpy( p_dir->name, name);
227 p_dir->help = p_dir->name + strlen(name) + 1;
228 strcpy(p_dir->help, help);
229 p_dir->sel = BCMCLI_ENTRY_DIR;
230 _bcmcli_choose_alias( parent, p_dir );
231 p_dir->access_right = access_right;
232 if (extras)
233 p_dir->u.dir.extras = *extras;
234
235 /* Add new directory to the parent's list */
236 p_dir->parent = parent;
237 p_e = &(parent->u.dir.first);
238 while (*p_e)
239 p_e = &((*p_e)->next);
240 *p_e = p_dir;
241
242 return p_dir;
243}
244
245static bcmcli_entry * find_entry_in_dir( bcmcli_entry *dir, const char *name,
246 bcmcli_entry_selector type, uint16_t recursive_search)
247{
248 bcmcli_entry *p1, *p;
249
250 if ( !dir )
251 {
252 dir = _bcmcli_root_dir;
253 if (!dir)
254 return NULL;
255 }
256 p = dir->u.dir.first;
257 while (p)
258 {
259 if ( !_bcmcli_stricmp(p->name, name, -1) && type == p->sel )
260 return p;
261 if ( recursive_search && p->sel == BCMCLI_ENTRY_DIR )
262 {
263 p1 = find_entry_in_dir(p, name , type, 1 );
264 if ( p1 )
265 return p1;
266 }
267 p = p->next;
268 }
269 return NULL;
270}
271
272
273/* Scan directory tree and look for directory with name starts from
274 * root directory with name root_name
275 */
276bcmcli_entry *bcmcli_dir_find(bcmcli_entry *parent, const char *name)
277{
278 if ( !parent )
279 parent = _bcmcli_root_dir;
280 return find_entry_in_dir(parent, name, BCMCLI_ENTRY_DIR, 0 );
281}
282
283
284/* Scan directory tree and look for command named "name". */
285bcmcli_entry *bcmcli_cmd_find(bcmcli_entry *parent, const char *name )
286{
287 if ( !parent )
288 parent = _bcmcli_root_dir;
289 return find_entry_in_dir(parent, name, BCMCLI_ENTRY_CMD, 0 );
290}
291
292
293/** Add CLI command
294 *
295 * \param[in] dir Handle of directory to add command to. NULL=root
296 * \param[in] name Command name
297 * \param[in] cmd_cb Command handler
298 * \param[in] help Help string
299 * \param[in] access_right Access rights
300 * \param[in] extras Optional extras
301 * \param[in] parms Optional parameters array. Must not be allocated on the stack!
302 * If parms!=NULL, the last parameter in the array must have name==NULL.
303 * \return
304 * 0 =OK\n
305 * <0 =error code
306 */
307bcmos_errno bcmcli_cmd_add(bcmcli_entry *dir, const char *name, bcmcli_cmd_cb cmd_cb,
308 const char *help, bcmcli_access_right access_right,
309 const bcmcli_cmd_extra_parm *extras, bcmcli_cmd_parm parms[])
310{
311 bcmcli_entry *p_token;
312 bcmcli_entry **p_e;
313 uint16_t i;
314 bcmcli_cmd_parm *parm = parms;
315
316 assert(name);
317 assert(help);
318 assert(cmd_cb);
319 if (!name || !cmd_cb || !help)
320 return BCM_ERR_PARM;
321
322 if (!_bcmcli_root_dir)
323 {
324 _bcmcli_alloc_root(NULL);
325 if (!_bcmcli_root_dir)
326 return BCM_ERR_NOMEM;
327 }
328
329 if (!dir)
330 dir = _bcmcli_root_dir;
331
332 p_token=(bcmcli_entry *)bcmos_calloc( sizeof(bcmcli_entry) + strlen(name) + strlen(help) + 2 );
333 if ( !p_token )
334 return BCM_ERR_NOMEM;
335
336 /* Copy name */
337 p_token->name = (char *)(p_token + 1);
338 strcpy( p_token->name, name );
339 p_token->help = p_token->name + strlen(name) + 1;
340 strcpy(p_token->help, help);
341 p_token->sel = BCMCLI_ENTRY_CMD;
342 p_token->u.cmd.cmd_cb = cmd_cb;
343 p_token->u.cmd.parms = parms;
344 if (extras)
345 p_token->u.cmd.extras = *extras;
346 p_token->access_right = access_right;
347
348 /* Convert name to lower case and choose alias */
349 _bcmcli_choose_alias(dir, p_token );
350
351
352 /* Check parameters */
353 for (i = 0; i < BCMCLI_MAX_PARMS && parms && parms[i].name; i++)
354 {
355 parm = &parms[i];
356 /* User-defined parameter must have a scan_cb callback for text->value conversion */
357 if ((parm->type==BCMCLI_PARM_USERDEF) && !parm->scan_cb)
358 {
359 bcmos_printf("MON: %s> scan_cb callback must be set for user-defined parameter %s\n", name, parm->name);
360 goto cmd_add_error;
361 }
362 if (parm->type==BCMCLI_PARM_ENUM || parm->type==BCMCLI_PARM_ENUM_MASK)
363 {
364 if (!parm->enum_table)
365 {
366 bcmos_printf("MON: %s> value table must be set in low_val for enum parameter %s\n", name, parm->name);
367 goto cmd_add_error;
368 }
369
370 /* Check default value if any */
371 if ((parm->flags & BCMCLI_PARM_FLAG_DEFVAL))
372 {
373 if (_bcmcli_enum_mask_scan_cb(parm, &parm->value, parm->value.string) < 0)
374 {
375 bcmos_printf("MON: %s> default value %s doesn't match any value of enum parameter %s\n", name, parm->value.string, parm->name);
376 goto cmd_add_error;
377 }
378 }
379 else if ((parm->flags & BCMCLI_PARM_FLAG_OPTIONAL))
380 {
381 /* Optional enum parameters are initialized by their 1st value by default.
382 * All other parameters are initialized to 0.
383 */
384 bcmcli_enum_val *values=parm->enum_table;
385 parm->value.enum_val = values[0].val;
386 }
387
388 /* All values of enum mask parameters mast be complementary bits */
389 if (parm->type==BCMCLI_PARM_ENUM_MASK)
390 {
391 long all_mask = 0;
392 bcmcli_enum_val *values;
393 for (values=parm->enum_table; values->name; ++values)
394 all_mask |= values->val;
395
396 for (values=parm->enum_table; values->name; ++values)
397 {
398 if ((all_mask & values->val) != values->val)
399 {
400 bcmos_printf("MON: %s> enum_table values of enum_mask parameters must be complementary bits\n", name, parm->name);
401 goto cmd_add_error;
402 }
403 all_mask &= ~values->val;
404 }
405 }
406 }
407 else if (parm->type==BCMCLI_PARM_BUFFER)
408 {
409 if (!parm->value.buffer.start || !parm->value.buffer.len)
410 {
411 bcmos_printf("MON: %s> value.buffer.start is not set for BUFFER parameter %s\n", name, parm->name);
412 goto cmd_add_error;
413 }
414 if (parm->max_array_size)
415 {
416 bcmos_printf("MON: %s> BUFFER arrays are not supported %s\n", name, parm->name);
417 goto cmd_add_error;
418 }
419 }
420 if (parm->max_array_size)
421 {
422 if (!parm->values)
423 {
424 bcmos_printf("MON: %s> parm->values must be set for parameter-array %s\n", name, parm->name);
425 goto cmd_add_error;
426 }
427 }
428 _bcmcli_assign_callbacks(parm);
429 }
430 if ((i == BCMCLI_MAX_PARMS) && parms[i].name[0])
431 {
432 bcmos_printf("MON: %s> too many parameters\n", name);
433 goto cmd_add_error;
434 }
435 p_token->u.cmd.num_parms = i;
436
437 /* Add token to the directory */
438 p_token->parent = dir;
439 p_e = &(dir->u.dir.first);
440 while (*p_e)
441 p_e = &((*p_e)->next);
442 *p_e = p_token;
443
444 return 0;
445
446cmd_add_error:
447 bcmos_free( p_token );
448 return BCM_ERR_PARM;
449}
450
451
452/** Destroy token (command or directory)
453 * \param[in] token Directory or command token. NULL=root
454 */
455void bcmcli_token_destroy(bcmcli_entry *token)
456{
457 if (!token)
458 {
459 if (!_bcmcli_root_dir)
460 return;
461 token = _bcmcli_root_dir;
462 }
463 /* Remove from parent's list */
464 if (token->parent)
465 {
466 bcmcli_entry **p_e;
467 p_e = &(token->parent->u.dir.first);
468 while (*p_e)
469 {
470 if (*p_e == token)
471 {
472 *p_e = token->next;
473 break;
474 }
475 p_e = &((*p_e)->next);
476 }
477 }
478
479 /* Remove all directory entries */
480 if (token->sel == BCMCLI_ENTRY_DIR)
481 {
482 bcmcli_entry *e = token->u.dir.first;
483 while ((e = token->u.dir.first))
484 bcmcli_token_destroy(e);
485 }
486 else if (token->u.cmd.extras.free_parms)
487 token->u.cmd.extras.free_parms(token->u.cmd.parms);
488
489 /* Release the token */
490 bcmos_free(token);
491
492 if (token == _bcmcli_root_dir)
493 {
494 _bcmcli_root_dir = NULL;
495 if (_bcmcli_root_session)
496 {
497 bcmcli_session_close(_bcmcli_root_session->session);
498 _bcmcli_root_session = NULL;
499 }
500 }
501}
502
503/** Open monitor session
504 *
505 * Monitor supports multiple simultaneous sessions with different
506 * access rights.
507 * Note that there already is a default session with full administrative rights,
508 * that takes input from stdin and outputs to stdout.
509 * \param[in] parm Session parameters. Must not be allocated on the stack.
510 * \param[out] p_session Session handle
511 * \return
512 * 0 =OK\n
513 * <0 =error code
514 */
515bcmos_errno bcmcli_session_open(const bcmcli_session_parm *parm, bcmcli_session **p_session)
516{
517 bcmcli_session *session;
518 bcmcli_session_extras *mon_session;
519 bcmcli_session_parm session_parms;
520 int rc;
521
522 assert(p_session);
523 if (!p_session)
524 return BCM_ERR_PARM;
525
526 if (!_bcmcli_root_dir)
527 {
528 _bcmcli_alloc_root(parm);
529 if (!_bcmcli_root_dir)
530 return BCM_ERR_NOMEM;
531 }
532 if (parm)
533 session_parms = *parm;
534 else
535 {
536 memset(&session_parms, 0, sizeof(session_parms));
537 session_parms.name = "unnamed";
538 }
539
540 /* Open comm session */
541 session_parms.extra_size = sizeof(bcmcli_session_extras);
542 rc = bcmcli_session_open_user(&session_parms, &session);
543 if (rc)
544 return rc;
545 mon_session = _bcmcli_session_data(session);
546 mon_session->curdir = _bcmcli_root_dir;
547 mon_session->session = session;
548
549 *p_session = session;
550
551 return 0;
552}
553
554#define BCMCLI_PARSE_RETURN(ret) \
555 do { \
556 rc = ret; \
557 goto bcmcli_parse_out; \
558 } while (0)
559
560/* Parse a single command. Stop on ';' or EOL */
561static bcmos_errno bcmcli_parse_command(bcmcli_session *session)
562{
563 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
564 bcmcli_entry *p_token;
565 bcmcli_name_value *pairs = NULL;
566 int stop_parsing = 0;
567 int npairs;
568 int i;
569 char *cmd_line;
570 bcmos_errno rc = BCM_ERR_OK;
571
572 session = mon_session->session;
573
574 /* Make a copy of command line - for logging */
575 cmd_line = bcmos_alloc(strlen(mon_session->p_inbuf) + 1);
576 if (!cmd_line)
577 return BCM_ERR_NOMEM;
578 strcpy(cmd_line, mon_session->p_inbuf);
579
580 /* Split string to name/value pairs */
581 rc = _bcmcli_split(mon_session, &pairs, &npairs);
582 if (rc)
583 {
584 if (rc == BCM_ERR_NOENT)
585 rc = BCM_ERR_OK;
586 BCMCLI_PARSE_RETURN(rc);
587 }
588
589 /* Interpret empty string as "display directory" */
590 if ( !npairs )
591 {
592 _bcmcli_display_dir(mon_session, mon_session->curdir );
593 BCMCLI_PARSE_RETURN(BCM_ERR_OK);
594 }
595
596 /* Identify parameters */
597 for (i=0; i<npairs && !rc && !stop_parsing; i++)
598 {
599 switch (pairs[i].type)
600 {
601 case BCMCLI_TOKEN_NAME:
602 case BCMCLI_TOKEN_VALUE:
603 /* Identify command. The 1st pair can't contain name, only value */
604 if (pairs[i].name)
605 {
606 bcmcli_session_print(session, "**ERR: %s is unexpected\n", pairs[i].name);
607 BCMCLI_PARSE_RETURN(BCM_ERR_PARM);
608 }
609 p_token = _bcmcli_search_token(mon_session->curdir, pairs[i].value);
610 if (p_token == NULL)
611 {
612 bcmcli_session_print(session, "**ERR: %s is unexpected\n", pairs[i].value);
613 BCMCLI_PARSE_RETURN(BCM_ERR_PARM);
614 }
615 /* Directory or command ? */
616 if (p_token->sel == BCMCLI_ENTRY_DIR)
617 {
618 mon_session->curdir = p_token;
619 _bcmcli_display_dir(mon_session, mon_session->curdir );
620 }
621 else
622 {
623 /* Function token */
624 mon_session->curcmd = p_token;
625 if (_bcmcli_parse_parms(mon_session, p_token, &pairs[i+1], npairs-i-1) < 0)
626 {
627 _bcmcli_help_entry(mon_session, p_token, &pairs[i+1], npairs-i-1, BCMOS_TRUE);
628 rc = BCM_ERR_PARM;
629 }
630 else
631 {
632 _bcmcli_log_cmd(cmd_line);
633 rc = p_token->u.cmd.cmd_cb(session, mon_session->cmd_parms, npairs-i-1 );
634 if (rc)
635 {
636 char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH];
637 bcmcli_session_print(session, "MON: %s> failed with error code %s(%d)\n",
638 _bcmcli_qualified_name(p_token, buffer, sizeof(buffer)),
639 bcmos_strerror(rc), rc);
640 }
641 _bcmcli_log_rc(rc);
642 _bcmcli_free_session_value_status(mon_session);
643 }
644 stop_parsing = 1;
645 }
646 break;
647
648 case BCMCLI_TOKEN_UP: /* Go to upper directory */
649 if (mon_session->curdir->parent)
650 mon_session->curdir = mon_session->curdir->parent;
651 _bcmcli_display_dir(mon_session, mon_session->curdir );
652 break;
653
654 case BCMCLI_TOKEN_ROOT: /* Go to the root directory */
655 mon_session->curdir = _bcmcli_root_dir;
656 _bcmcli_display_dir(mon_session, mon_session->curdir );
657 break;
658
659 case BCMCLI_TOKEN_HELP: /* Display help */
660 if (i < npairs-1 &&
661 ((p_token = _bcmcli_search_token( mon_session->curdir, pairs[i+1].value)) != NULL ))
662 {
663 _bcmcli_help_entry(mon_session, p_token, &pairs[i+2], npairs-i-2, BCMOS_FALSE);
664 }
665 else
666 {
667 _bcmcli_help_dir(mon_session, mon_session->curdir);
668 }
669 stop_parsing = 1;
670 break;
671
672 default:
673 stop_parsing = 1;
674 break;
675 }
676 }
677
678bcmcli_parse_out:
679 if (pairs)
680 bcmos_free(pairs);
681 if (cmd_line)
682 bcmos_free(cmd_line);
683 return rc;
684
685}
686
687/** Context extension */
688bcmos_errno bcmcli_extend(bcmcli_session *session, char *input_str, char *insert_str, uint32_t insert_size)
689{
690 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
691 bcmcli_entry *p_token;
692 bcmcli_name_value *pairs;
693 bcmos_bool last_is_space;
694 int npairs;
695 bcmos_errno rc = BCM_ERR_OK;
696
697 if (!mon_session || !mon_session->curdir || !input_str)
698 return BCM_ERR_PARM;
699
700 insert_str[0] = 0;
701 mon_session->p_inbuf = input_str;
702
703 last_is_space = strlen(input_str) && (input_str[strlen(input_str) - 1] == ' ');
704
705 /* Split string to name/value pairs */
706 rc = _bcmcli_split(mon_session, &pairs, &npairs);
707 if (rc)
708 return rc;
709
710 /* empty list - display list of commands */
711 if ( !npairs )
712 {
713 _bcmcli_display_dir(mon_session, mon_session->curdir );
714 BCMCLI_PARSE_RETURN(0);
715 }
716
717 /* Identify parameters */
718 switch (pairs[0].type)
719 {
720 case BCMCLI_TOKEN_NAME:
721 case BCMCLI_TOKEN_VALUE:
722 /* Identify command. The 1st pair can't contain name, only value */
723 if (pairs[0].name ||
724 !(p_token = _bcmcli_search_token(mon_session->curdir, pairs[0].value)))
725 {
726 _bcmcli_display_dir(mon_session, mon_session->curdir );
727 BCMCLI_PARSE_RETURN(BCM_ERR_PARM);
728 }
729
730 /* Directory or command ? */
731 if (p_token->sel != BCMCLI_ENTRY_CMD)
732 BCMCLI_PARSE_RETURN(BCM_ERR_OK);
733
734 /* Function token */
735 mon_session->curcmd = p_token;
736 rc = _bcmcli_extend_parms(mon_session, &pairs[1], npairs-1, last_is_space, insert_str, insert_size);
737 break;
738
739 default:
740 break;
741 }
742
743bcmcli_parse_out:
744 bcmos_free(pairs);
745 return rc;
746}
747
748/** Parse and execute input string.
749 * input_string can contain multiple commands delimited by ';'
750 *
751 * \param[in] session Session handle
752 * \param[in] input_string String to be parsed. May consist of multiple ';'-delimited commands
753 * \return
754 * =0 - OK \n
755 * BCM_ERR_PARM - parsing error\n
756 * other - return code - as returned from command handler.
757 * It is recommended to return -EINTR to interrupt monitor loop.
758 */
759bcmos_errno bcmcli_parse(bcmcli_session *session, char* input_string)
760{
761 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
762 uint32_t input_len;
763 int rc = 0;
764
765 if (!mon_session || !mon_session->curdir || !input_string)
766 return BCM_ERR_PARM;
767 input_len = strlen(input_string);
768 if (!input_len)
769 return 0;
770
771 /* cut CR, LF if any */
772 while (input_len && (input_string[input_len-1]=='\n' || input_string[input_len-1]=='\r'))
773 input_string[--input_len]=0;
774
775 mon_session->p_inbuf = input_string;
776 mon_session->stop_monitor = 0;
777
778 do {
779 rc = bcmcli_parse_command(session);
780 } while (mon_session->p_inbuf && mon_session->p_inbuf[0] && !mon_session->stop_monitor && !rc);
781
782 return rc;
783}
784
785/** Read input and parse iteratively until EOF or bcmcli_is_stopped()
786 *
787 * \param[in] session Session handle
788 * \return
789 * =0 - OK \n
790 */
791bcmos_errno bcmcli_driver(bcmcli_session *session)
792{
793 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
794
795 session = mon_session->session;
796 mon_session->stop_monitor = 0;
797 while (!bcmcli_is_stopped(session) &&
798 bcmcli_session_gets(session, mon_session->inbuf, sizeof(mon_session->inbuf)-1))
799 {
800 /* Session could've been stopped while in "gets". Check again and proceed if active */
801 if (!bcmcli_is_stopped(session))
802 bcmcli_parse(session, mon_session->inbuf);
803 }
804
805 return BCM_ERR_OK;
806}
807
808
809/* Stop monitor driver */
810void bcmcli_stop( bcmcli_session *session )
811{
812 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
813 assert(mon_session);
814 mon_session->stop_monitor = 1;
815}
816
817/** Returns 1 if monitor session is stopped
818 * \param[in] session Session handle
819 * \returns 1 if monitor session stopped by bcmcli_stop()\n
820 * 0 otherwise
821 */
822bcmos_bool bcmcli_is_stopped(bcmcli_session *session)
823{
824 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
825 return mon_session->stop_monitor;
826}
827
828
829/** Get parameter number given its name.
830 * The function is intended for use by command handlers
831 * \param[in] session Session handle
832 * \param[in,out] parm_name Parameter name
833 * \return
834 * >=0 - parameter number\n
835 * <0 - parameter with this name doesn't exist
836 */
837int bcmcli_parm_number(bcmcli_session *session, const char *parm_name)
838{
839 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
840 int i;
841 if (!parm_name || !mon_session || !mon_session->curcmd)
842 return BCM_ERR_PARM;
843 for(i=0;
844 mon_session->cmd_parms[i].name &&
845 _bcmcli_stricmp( parm_name, mon_session->cmd_parms[i].name, -1);
846 i++)
847 ;
848 if (!mon_session->cmd_parms[i].name)
849 return BCM_ERR_PARM;
850 return i;
851}
852
853
854/** Get parameter by name
855 * The function is intended for use by command handlers
856 * \param[in] session Session handle
857 * \param[in] parm_name Parameter name
858 * \return
859 * parameter pointer or NULL if not found
860 */
861bcmcli_cmd_parm *bcmcli_parm_get(bcmcli_session *session, const char *parm_name)
862{
863 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
864 int nparm = bcmcli_parm_number(session, parm_name);
865 if (nparm < 0)
866 {
867 return NULL;
868 }
869 return &mon_session->cmd_parms[nparm];
870}
871
872
873/** Check if parameter is set
874 * \param[in] session Session handle
875 * \param[in] parm_number Parameter number
876 * \return
877 * 1 if parameter is set\n
878 * 0 if parameter is not set or parm_number is invalid
879 */
880bcmos_errno bcmcli_parm_check(bcmcli_session *session, int parm_number)
881{
882 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
883
884 if (parm_number < 0 || !mon_session || !mon_session->curcmd)
885 return BCM_ERR_PARM;
886 if (parm_number >= mon_session->num_parms)
887 return BCM_ERR_PARM;
888 if (!(mon_session->cmd_parms[parm_number].flags & BCMCLI_PARM_FLAG_ASSIGNED))
889 return BCM_ERR_NOENT;
890 return BCM_ERR_OK;
891}
892
893
894/** Get enum's string value given its internal value
895 * \param[in] session Session handle
896 * \param[in] parm_number Parameter number
897 * \param[in] value Internal value
898 * \return
899 * enum string value or NULL if parameter is not enum or
900 * internal value is invalid
901 */
902const char *bcmcli_enum_parm_stringval(bcmcli_session *session, int parm_number, long value)
903{
904 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
905 int i;
906 bcmcli_enum_val *values;
907 if (parm_number < 0 || !mon_session || !mon_session->curcmd)
908 return NULL;
909 for(i=0; i<parm_number && mon_session->cmd_parms[i].name; i++)
910 ;
911 if (i < parm_number)
912 return NULL;
913 if (mon_session->cmd_parms[parm_number].type != BCMCLI_PARM_ENUM)
914 return NULL;
915 values = mon_session->cmd_parms[parm_number].enum_table;
916 return bcmcli_enum_stringval(values, value);
917}
918
919
920/* Get current directory handle */
921bcmcli_entry *bcmcli_dir_get(bcmcli_session *session)
922{
923 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
924 if (!mon_session)
925 return NULL;
926 return mon_session->curdir;
927}
928
929/* Set current directory */
930bcmos_errno bcmcli_dir_set(bcmcli_session *session, bcmcli_entry *dir)
931{
932 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
933 assert(mon_session);
934 if (!mon_session)
935 return BCM_ERR_PARM;
936 /* Check access rights */
937 if (!dir)
938 dir = _bcmcli_root_dir;
939 if (dir->access_right > bcmcli_session_access_right(mon_session->session))
940 return BCM_ERR_PERM;
941 mon_session->curdir = dir;
942 return 0;
943}
944
945/** Get token name
946 * \param[in] token Directory or command token
947 * \return directory token name
948 */
949const char *bcmcli_token_name(bcmcli_entry *token)
950{
951 if (!token)
952 return NULL;
953 return token->name;
954}
955
956bcmcli_cmd_parm *bcmcli_find_named_parm(bcmcli_session *session, const char *name)
957{
958 bcmcli_cmd_parm * cmd_parm;
959
960 if ( !session || !name || *name=='\0')
961 return NULL;
962
963 cmd_parm = _bcmcli_find_named_parm(_bcmcli_session_data(session), name);
964 if(cmd_parm && (cmd_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
965 {
966 return cmd_parm;
967 }
968
969 return NULL;
970}
971
972/* Return TRUE if parameter value is set */
973bcmos_bool bcmcli_parm_value_is_set(bcmcli_session *session, bcmcli_cmd_parm *parm, uint32_t value_index)
974{
975 bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
976 uint32_t parm_index = parm - mon_session->cmd_parms;
977
978 if (!mon_session)
979 {
980 bcmcli_print(NULL, "MON> Session %p is invalid\n", session);
981 return BCMOS_FALSE;
982 }
983
984 parm_index = parm - mon_session->cmd_parms;
985 if (parm_index >= BCMCLI_MAX_PARMS)
986 {
987 bcmcli_print(session, "MON> Parameter %p is invalid\n", parm);
988 return BCMOS_FALSE;
989 }
990
991 if (!parm->array_size)
992 return mon_session->value_status[parm_index].value_set;
993
994 if (value_index >= parm->array_size)
995 return BCMOS_FALSE;
996
997 return mon_session->value_status[parm_index].values_set[value_index];
998}
999
1000bcmcli_cmd_parm *bcmcli_find_parm_by_prefix(bcmcli_session *session, const char *prefix)
1001{
1002 bcmcli_cmd_parm *cmd_parm;
1003
1004 if (session == NULL || prefix == NULL)
1005 {
1006 return NULL;
1007 }
1008
1009 cmd_parm = _bcmcli_session_data(session)->cmd_parms;
1010 while (cmd_parm->name != NULL)
1011 {
1012 if ((_bcmcli_stricmp(prefix, cmd_parm->name, strlen(prefix)) == 0) &&
1013 ((cmd_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED) != 0))
1014 {
1015 return cmd_parm;
1016 }
1017 ++cmd_parm;
1018 }
1019
1020 return NULL;
1021}
1022
1023/** Print CLI parameter
1024 * \param[in] session Session handle
1025 * \param[in] parm Parameter
1026 */
1027void bcmcli_parm_print(bcmcli_session *session, const bcmcli_cmd_parm *parm)
1028{
1029 char buf[BCMCLI_MAX_PARM_VAL_LEN] = "";
1030 if (parm->type != BCMCLI_PARM_BUFFER)
1031 {
1032 parm->format_cb(parm, parm->value, buf, sizeof(buf));
1033 bcmcli_print(session, "%s\n", buf);
1034 }
1035 else
1036 {
1037 if (parm->value.buffer.len == 0)
1038 {
1039 bcmcli_print(session, "-\n");
1040 }
1041 else
1042 {
1043 bcmcli_print(session, "\n");
1044 bcmcli_session_hexdump(session, parm->value.buffer.start, 0,
1045 bcmolt_buf_get_used(&parm->value.buffer), NULL);
1046 }
1047 }
1048}
1049
1050/** Enable / disable CLI command logging
1051 * \param[in] mode Logging flags
1052 * \param[in] log Log session. Must be set if mode != BCMCLI_CMD_LOG_NONE
1053 * \return 0=OK or error <0
1054 */
1055bcmos_errno bcmcli_log_set(bcmcli_log_mode mode, bcmcli_session *log)
1056{
1057 if (mode != BCMCLI_LOG_NONE && log == NULL)
1058 {
1059 BCMOS_TRACE_ERR("log session must be set\n");
1060 return BCM_ERR_PARM;
1061 }
1062 if (mode == BCMCLI_LOG_NONE)
1063 {
1064 _bcmcli_log_session = NULL;
1065 }
1066 else
1067 {
1068 _bcmcli_log_session = log;
1069 }
1070 _bcmcli_log_mode = mode;
1071 return BCM_ERR_OK;
1072}
1073
1074/** Write string to CLI log.
1075 * The function is ignored if CLI logging is not enabled using bcmcli_log_set()
1076 * \param[in] format printf-like format followed by arguments
1077 */
1078void bcmcli_log(const char *format, ...)
1079{
1080 va_list ap;
1081 if (!_bcmcli_log_session)
1082 return;
1083 va_start(ap, format);
1084 bcmcli_session_vprint(_bcmcli_log_session, format, ap);
1085 va_end(ap);
1086}
1087
1088/*********************************************************/
1089/* Internal functions */
1090/*********************************************************/
1091
1092static void _bcmcli_log_cmd(const char *cmd)
1093{
1094 switch (_bcmcli_log_mode)
1095 {
1096 case BCMCLI_LOG_CLI:
1097 bcmcli_log("%s\n", cmd);
1098 break;
1099 case BCMCLI_LOG_C_COMMENT:
1100 bcmcli_log("/* %s */\n", cmd);
1101 break;
1102 default:
1103 break;
1104 }
1105}
1106
1107static void _bcmcli_log_rc(bcmos_errno rc)
1108{
1109 switch (_bcmcli_log_mode)
1110 {
1111 case BCMCLI_LOG_CLI:
1112 bcmcli_log("# CLI command completed: %s (%d)\n", bcmos_strerror(rc), rc);
1113 break;
1114 case BCMCLI_LOG_C_COMMENT:
1115 bcmcli_log("/* CLI command completed: %s (%d) */\n", bcmos_strerror(rc), rc);
1116 break;
1117 default:
1118 break;
1119 }
1120
1121}
1122
1123static bcmcli_session *_bcmcli_help_session_open(bcmcli_session *main_session)
1124{
1125 bcmolt_string *help_scratch;
1126 bcmcli_session *help_session;
1127 bcmos_errno err;
1128
1129 bcmolt_string_create(&help_scratch, BCMCLI_HELP_BUFFER_SIZE);
1130 err = bcmcli_session_open_string(&help_session, help_scratch);
1131 if (err)
1132 {
1133 bcmcli_session_print(main_session, "CLI: can't create help session. Error %s\n", bcmos_strerror(err));
1134 return NULL;
1135 }
1136
1137 return help_session;
1138}
1139
1140static void _bcmcli_help_session_print_and_close(bcmcli_session *main_session, bcmcli_session *help_session)
1141{
1142 bcmolt_string *str = bcmcli_session_user_priv(help_session);
1143
1144 bcmcli_session_print(main_session, "%s", bcmolt_string_get(str));
1145 bcmcli_session_close(help_session);
1146 bcmolt_string_destroy(str);
1147}
1148
1149#ifdef CONFIG_LINENOISE
1150static bcmos_errno _bcmcli_line_edit_cmd(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
1151{
1152 if (n_parms > 0)
1153 {
1154 if ((parm[0].flags & BCMCLI_PARM_FLAG_ASSIGNED))
1155 linenoiseSetDumbTerminal(session->ln_session, ! parm[0].value.number);
1156 if ((parm[1].flags & BCMCLI_PARM_FLAG_ASSIGNED))
1157 linenoiseSetMultiLine(session->ln_session, parm[1].value.number);
1158 }
1159 else
1160 {
1161 int dumb = linenoiseGetDumbTerminal(session->ln_session);
1162 int multiline = linenoiseGetMultiLine(session->ln_session);
1163 bcmcli_session_print(session, "Line editing: %s Multiline: %s\n",
1164 dumb ? "off" : "on", multiline ? "on" : "off");
1165 }
1166 return BCM_ERR_OK;
1167}
1168#endif
1169
1170/* Allocate root directory and default session */
1171static void _bcmcli_alloc_root(const bcmcli_session_parm *first_session_parm)
1172{
1173 bcmcli_session_parm session_parms;
1174 bcmcli_session *session;
1175 int rc;
1176
1177 /* The very first call. Allocate root structure */
1178 if ((_bcmcli_root_dir=(bcmcli_entry *)bcmos_calloc(sizeof(bcmcli_entry) + strlen(BCMCLI_ROOT_HELP) + 2 )) == NULL)
1179 return;
1180 _bcmcli_root_dir->name = (char *)(_bcmcli_root_dir + 1);
1181 _bcmcli_root_dir->help = (char *)(_bcmcli_root_dir->name + 1);
1182 strcpy(_bcmcli_root_dir->help, BCMCLI_ROOT_HELP);
1183 _bcmcli_root_dir->sel = BCMCLI_ENTRY_DIR;
1184 _bcmcli_root_dir->access_right = BCMCLI_ACCESS_GUEST;
1185
1186 memset(&session_parms, 0, sizeof(session_parms));
1187 session_parms.access_right = BCMCLI_ACCESS_ADMIN;
1188 session_parms.extra_size = sizeof(bcmcli_session_extras);
1189 session_parms.name = "monroot";
1190 if (first_session_parm)
1191 {
1192 session_parms.line_edit_mode = first_session_parm->line_edit_mode;
1193 }
1194 rc = bcmcli_session_open(&session_parms, &session);
1195 if (rc)
1196 {
1197 bcmos_free(_bcmcli_root_dir);
1198 _bcmcli_root_dir = NULL;
1199 _bcmcli_root_session = NULL;
1200 return;
1201 }
1202 _bcmcli_root_session = _bcmcli_session_data(session);
1203 _bcmcli_root_session->session = session;
1204 _bcmcli_root_session->curdir = _bcmcli_root_dir;
1205
1206 /* Add command to disable/enable line editing */
1207#ifdef CONFIG_LINENOISE
1208 if (session_parms.line_edit_mode != BCMCLI_LINE_EDIT_DISABLE)
1209 {
1210 BCMCLI_MAKE_CMD(NULL, "~", "Enable/disable/query line editing", _bcmcli_line_edit_cmd,
1211 BCMCLI_MAKE_PARM_ENUM("enable", "Enable line editing", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL),
1212 BCMCLI_MAKE_PARM_ENUM("multiline", "Enable multiline mode", bcmcli_enum_bool_table,
1213 BCMCLI_PARM_FLAG_OPTIONAL));
1214 }
1215#endif
1216}
1217
1218/* Display directory */
1219static void _bcmcli_display_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir)
1220{
1221 bcmcli_session *session = mon_session->session;
1222 bcmcli_entry *p_token;
1223 bcmcli_entry *prev=NULL;
1224 bcmcli_session *help_session = _bcmcli_help_session_open(session);
1225
1226 if (!help_session)
1227 return;
1228
1229 bcmcli_session_print(help_session, "%s%s> ", (p_dir==_bcmcli_root_dir)?"":".../", p_dir->name );
1230 p_token = p_dir->u.dir.first;
1231 while ( p_token )
1232 {
1233 if (p_token->access_right <= bcmcli_session_access_right(session))
1234 {
1235 if (prev)
1236 bcmcli_session_print(help_session, ", ");
1237 bcmcli_session_print(help_session, "%s", p_token->name );
1238 if (p_token->sel == BCMCLI_ENTRY_DIR )
1239 bcmcli_session_print(help_session, "/");
1240 prev = p_token;
1241 }
1242 p_token = p_token->next;
1243 }
1244 bcmcli_session_print(help_session, "\n");
1245 _bcmcli_help_session_print_and_close(session, help_session);
1246}
1247
1248
1249/* Is character that can be used in a single token ? */
1250static inline int _bcmcli_is_special_char(char c)
1251{
1252 if (!c)
1253 return 0;
1254 return (c == BCMCLI_HELP_CHAR || c == BCMCLI_COMMENT_CHAR || c == BCMCLI_EQUAL_CHAR);
1255}
1256
1257/* Make a preliminary analizis of <name> token.
1258 * Returns a token type (Empty, Up, Root, Break, Name)
1259 */
1260static bcmcli_token_type _bcmcli_analyze_token( const char *name )
1261{
1262 if (!name[0] || name[0]==';')
1263 return BCMCLI_TOKEN_EMPTY;
1264
1265 if (*name == BCMCLI_COMMENT_CHAR)
1266 return BCMCLI_TOKEN_BREAK;
1267
1268 if (!strcmp(name, BCMCLI_UP_STR))
1269 return BCMCLI_TOKEN_UP;
1270
1271 if (!strcmp(name, BCMCLI_ROOT_STR))
1272 return BCMCLI_TOKEN_ROOT;
1273
1274 if (*name == BCMCLI_HELP_CHAR)
1275 return BCMCLI_TOKEN_HELP;
1276
1277 return BCMCLI_TOKEN_VALUE;
1278
1279}
1280
1281
1282/* isspace wrapper */
1283static inline int _bcmcli_isspace(char c)
1284{
1285 return isspace((int)c);
1286}
1287
1288/* Cut the first word from <p_inbuf>.
1289 * - Return pointer to start of the word in p_word
1290 * - 0 terminator is inserted in the end of the word
1291 * - session->p_inbuf is updated to point after the word
1292 * Returns token type
1293 */
1294static bcmcli_token_type _bcmcli_get_word(bcmcli_session_extras *mon_session, char **buf, char **p_word)
1295{
1296 bcmcli_token_type token_type;
1297 char *p_inbuf = *buf;
1298 char next_char = 0;
1299 bcmos_bool quoted_string = BCMOS_FALSE;
1300
1301 /* Skip leading blanks */
1302 while (*p_inbuf && (_bcmcli_isspace(*p_inbuf) || (*p_inbuf==',')))
1303 ++p_inbuf;
1304
1305 *buf = p_inbuf;
1306 if (! *p_inbuf)
1307 return BCMCLI_TOKEN_EMPTY;
1308 if (*p_inbuf == ';')
1309 {
1310 *p_inbuf = 0;
1311 *buf = ++p_inbuf;
1312 return BCMCLI_TOKEN_EMPTY;
1313 }
1314
1315 /* Quoted string ? */
1316 if (*p_inbuf == '"')
1317 {
1318 quoted_string = BCMOS_TRUE;
1319 *p_word = ++p_inbuf;
1320 while ( *p_inbuf && *p_inbuf!='"' )
1321 ++p_inbuf;
1322 if (*p_inbuf != '"')
1323 {
1324 bcmcli_session_print(mon_session->session, "MON: unterminated string %s\n", *p_word);
1325 return BCMCLI_TOKEN_EMPTY;
1326 }
1327 if (*p_inbuf)
1328 *(p_inbuf++) = 0;
1329 }
1330 else
1331 {
1332 *p_word = p_inbuf;
1333 if (!_bcmcli_is_special_char(*p_inbuf))
1334 {
1335 do ++p_inbuf;
1336 while (*p_inbuf && !_bcmcli_isspace(*p_inbuf) && *p_inbuf!=';' && !_bcmcli_is_special_char(*p_inbuf));
1337 /* Skip trailing spaces */
1338 while (*p_inbuf && _bcmcli_isspace(*p_inbuf))
1339 *(p_inbuf++) = 0;
1340 next_char = *p_inbuf;
1341 if (next_char == BCMCLI_EQUAL_CHAR)
1342 *(p_inbuf++) = 0;
1343 }
1344 else
1345 {
1346 ++p_inbuf;
1347 }
1348 }
1349 *buf = p_inbuf;
1350 token_type = _bcmcli_analyze_token( *p_word );
1351 if (token_type == BCMCLI_TOKEN_VALUE && next_char == BCMCLI_EQUAL_CHAR)
1352 token_type = BCMCLI_TOKEN_NAME;
1353 if ((token_type == BCMCLI_TOKEN_EMPTY) && quoted_string)
1354 token_type = BCMCLI_TOKEN_VALUE;
1355 return token_type;
1356}
1357
1358/* Split string to [name=]value pairs */
1359static bcmos_errno _bcmcli_split(bcmcli_session_extras *mon_session, bcmcli_name_value **p_pairs, int *p_npairs)
1360{
1361 bcmcli_name_value *pairs;
1362 char *tmp_buf, *tmp_buf_org;
1363 char *word;
1364 bcmcli_token_type token_type, prev_type=BCMCLI_TOKEN_EMPTY;
1365 int n = 0;
1366
1367 /* Make a copy of input buffer */
1368 tmp_buf_org = tmp_buf = bcmos_alloc(strlen(mon_session->p_inbuf) + 1);
1369 if (!tmp_buf)
1370 return BCM_ERR_NOMEM;
1371 strcpy(tmp_buf, mon_session->p_inbuf);
1372
1373 /* Calculate number of pairs first */
1374 token_type = _bcmcli_get_word(mon_session, &tmp_buf, &word);
1375 while (token_type != BCMCLI_TOKEN_EMPTY && token_type != BCMCLI_TOKEN_BREAK)
1376 {
1377 /* Skip =value */
1378 if (!(prev_type == BCMCLI_TOKEN_NAME && token_type == BCMCLI_TOKEN_VALUE))
1379 ++n;
1380 prev_type = token_type;
1381 token_type = _bcmcli_get_word(mon_session, &tmp_buf, &word);
1382 }
1383 bcmos_free(tmp_buf_org);
1384 *p_npairs = n;
1385 if (!n)
1386 {
1387 *p_pairs = NULL;
1388 /* Cut input string in order to prevent infinite loop in the parser if the string
1389 * is not empty (e.g., contains spaces) */
1390 *mon_session->p_inbuf = 0;
1391 if (token_type == BCMCLI_TOKEN_BREAK)
1392 {
1393 return BCM_ERR_NOENT;
1394 }
1395 return 0;
1396 }
1397
1398 *p_pairs = pairs = bcmos_calloc(n * sizeof(bcmcli_name_value));
1399 if (! pairs)
1400 return BCM_ERR_NOMEM;
1401
1402 /* Now scan the original string and set names and values */
1403 token_type = _bcmcli_get_word(mon_session, &mon_session->p_inbuf, &word);
1404 prev_type=BCMCLI_TOKEN_EMPTY;
1405 --pairs; /* it is going to be pre-incremented */
1406 while (token_type != BCMCLI_TOKEN_EMPTY && token_type != BCMCLI_TOKEN_BREAK)
1407 {
1408 if (!(prev_type == BCMCLI_TOKEN_NAME && token_type == BCMCLI_TOKEN_VALUE))
1409 ++pairs;
1410 pairs->type = token_type;
1411 if (token_type == BCMCLI_TOKEN_NAME)
1412 {
1413 pairs->name = word;
1414 }
1415 else
1416 {
1417 pairs->value = word;
1418 }
1419 prev_type = token_type;
1420 token_type = _bcmcli_get_word(mon_session, &mon_session->p_inbuf, &word);
1421 }
1422 return 0;
1423}
1424
1425/* Find parameter by name */
1426static bcmcli_cmd_parm *_bcmcli_find_named_parm(bcmcli_session_extras *mon_session, const char *name)
1427{
1428 bcmcli_cmd_parm *cmd_parm = mon_session->cmd_parms;
1429
1430 while (cmd_parm->name)
1431 {
1432 if (!_bcmcli_stricmp(name, cmd_parm->name, -1))
1433 break;
1434 ++cmd_parm;
1435 }
1436
1437 if (!cmd_parm->name)
1438 return NULL;
1439
1440 return cmd_parm;
1441}
1442
1443/* Extend session parameter table based on selector value */
1444static bcmos_errno _bcmcli_extend_parm_table(bcmcli_session_extras *mon_session,
1445 bcmcli_cmd_parm *selector, const char *value)
1446{
1447 bcmcli_enum_val *values=selector->enum_table;
1448 bcmcli_cmd_parm *parms;
1449 bcmcli_cmd_parm *session_parm;
1450 int nsel = selector - mon_session->cmd_parms;
1451 bcmcli_parm_value_status *val_status;
1452 int nparms;
1453
1454 while (values->name)
1455 {
1456 if (!_bcmcli_stricmp(values->name, value, -1))
1457 break;
1458 ++values;
1459 }
1460 if (!values->name)
1461 return BCM_ERR_INTERNAL;
1462
1463 /* Calculate number of parameters in selected table */
1464 parms = values->parms;
1465 while (parms && parms->name)
1466 {
1467 ++parms;
1468 }
1469 nparms = parms - values->parms;
1470
1471 if (mon_session->num_parms + nparms >= BCMCLI_MAX_PARMS)
1472 {
1473 bcmcli_session_print(mon_session->session, "MON: %s> Can's process selector %s. Too many parameters\n",
1474 mon_session->curcmd->name, selector->name);
1475 return BCM_ERR_OVERFLOW;
1476 }
1477
1478 /* Shift session parameters making room for the new table */
1479 if (selector != &mon_session->cmd_parms[mon_session->num_parms-1])
1480 {
1481 memmove(selector + nparms + 1, selector + 1,
1482 (&mon_session->cmd_parms[mon_session->num_parms-1] - selector) * sizeof(bcmcli_cmd_parm));
1483 memmove(&mon_session->value_status[nsel + nparms + 1], &mon_session->value_status[nsel + 1],
1484 (mon_session->num_parms - nsel) * sizeof(bcmcli_parm_value_status));
1485 }
1486
1487 /* Finally insert selector's table */
1488 parms = values->parms;
1489 session_parm = selector+1;
1490 val_status = &mon_session->value_status[nsel + 1];
1491 while (parms && parms->name)
1492 {
1493 *session_parm = *parms;
1494 _bcmcli_assign_callbacks(session_parm);
1495
1496 if (parms->max_array_size)
1497 {
1498 val_status->values_set = bcmos_calloc(sizeof(bcmos_bool) * parms->max_array_size);
1499 if (!val_status->values_set)
1500 {
1501 bcmcli_session_print(mon_session->session, "MON: > Couldn't allocate value status array for %s\n",
1502 parms->name);
1503 return BCM_ERR_NOMEM;
1504 }
1505 }
1506 else
1507 {
1508 val_status->value_set = BCMOS_FALSE;
1509 }
1510
1511
1512 ++parms;
1513 ++session_parm;
1514 ++val_status;
1515 }
1516 mon_session->num_parms += nparms;
1517
1518 return BCM_ERR_OK;
1519}
1520
1521/* Parse a single parameter value (scalar value or array element) */
1522static bcmos_errno _bcmcli_parse_1value(bcmcli_session_extras *mon_session, bcmcli_entry *cmd,
1523 bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_value,
1524 int val_len, bcmos_bool suppress_err_print)
1525{
1526 bcmos_errno rc;
1527
1528 if (val_len >= 0)
1529 {
1530 /* We are dealing with array element. string_value is comma rather than
1531 * 0-terminated. Copy it aside.
1532 */
1533 char val_copy[val_len + 1];
1534 strncpy(val_copy, string_value, val_len);
1535 val_copy[val_len] = 0;
1536 rc = parm->scan_cb(parm, value, val_copy);
1537 }
1538 else
1539 {
1540 rc = parm->scan_cb(parm, value, string_value);
1541 }
1542 if (rc)
1543 {
1544 if (!suppress_err_print)
1545 {
1546 bcmcli_session_print(mon_session->session, "MON: %s> <%s>: value %s is invalid\n",
1547 cmd->name, parm->name, string_value);
1548 }
1549 }
1550 return rc;
1551}
1552
1553
1554/* Parse parameter value, including array value (comma-delimited list of element values) */
1555static bcmos_errno _bcmcli_parse_value(bcmcli_session_extras *mon_session, bcmcli_entry *cmd,
1556 bcmcli_cmd_parm *parm, const char *string_value, bcmos_bool suppress_err_print)
1557{
1558 bcmos_errno rc = BCM_ERR_OK;
1559 uint32_t parm_index = parm - mon_session->cmd_parms;
1560
1561 BUG_ON(parm_index >= BCMCLI_MAX_PARMS);
1562
1563 if (parm->max_array_size)
1564 {
1565 uint32_t i = 0;
1566
1567 /* Empty array? */
1568 if (_bcmcli_stricmp(string_value, BCMCLI_ARRAY_EMPTY, -1))
1569 {
1570 /* array element values are comma-delimited */
1571 for (i = 0; i < parm->max_array_size && string_value && *string_value && !rc; i++)
1572 {
1573 const char *pcomma;
1574 int val_len;
1575
1576 pcomma = strchr(string_value, BCMCLI_ARRAY_DELIM_CHAR);
1577 if (pcomma)
1578 {
1579 val_len = pcomma - string_value;
1580 }
1581 else
1582 {
1583 val_len = -1; /* to the end of string */
1584 }
1585 /* No value ? */
1586 if (_bcmcli_stricmp(string_value, BCMCLI_PARM_NO_VALUE, val_len))
1587 {
1588 rc = _bcmcli_parse_1value(mon_session, cmd,
1589 parm, &parm->values[i], string_value, val_len, suppress_err_print);
1590 mon_session->value_status[parm_index].values_set[i] = (rc == BCM_ERR_OK);
1591 }
1592 string_value = pcomma ? pcomma + 1 : NULL;
1593 }
1594 /* If all parsed values were ok, but we have more values than array size - it is an error */
1595 if (string_value && *string_value && !rc)
1596 {
1597 rc = BCM_ERR_TOO_MANY;
1598 if (!suppress_err_print)
1599 {
1600 bcmcli_session_print(mon_session->session, "MON: %s> <%s>: too many values. %s is invalid\n",
1601 cmd->name, parm->name, string_value);
1602 }
1603 }
1604 }
1605
1606 parm->array_size = i;
1607 }
1608 else
1609 {
1610 if (_bcmcli_stricmp(string_value, BCMCLI_PARM_NO_VALUE, strlen(string_value)))
1611 {
1612 rc = _bcmcli_parse_1value(mon_session, cmd,
1613 parm, &parm->value, string_value, -1, suppress_err_print);
1614 mon_session->value_status[parm_index].value_set = (rc == BCM_ERR_OK);
1615 }
1616 }
1617
1618 return rc;
1619}
1620
1621/* Release value status arrays */
1622static void _bcmcli_free_session_value_status(bcmcli_session_extras *mon_session)
1623{
1624 bcmcli_cmd_parm *parms=mon_session->cmd_parms;
1625 int i;
1626
1627 for (i = 0; i < BCMCLI_MAX_PARMS; i++)
1628 {
1629 if (parms[i].max_array_size)
1630 {
1631 if (mon_session->value_status[i].values_set != NULL)
1632 {
1633 bcmos_free(mon_session->value_status[i].values_set);
1634 mon_session->value_status[i].values_set = NULL;
1635 }
1636 }
1637 else
1638 {
1639 mon_session->value_status[i].value_set = BCMOS_FALSE;
1640 }
1641 }
1642}
1643
1644/* Populate session parameters. Apply selectors */
1645static bcmos_errno _bcmcli_populate_parms(bcmcli_session_extras *mon_session, bcmcli_entry *cmd,
1646 bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print, int *last)
1647{
1648 const char *parm_value;
1649 int positional=1;
1650 bcmcli_cmd_parm *parms=mon_session->cmd_parms;
1651 bcmcli_cmd_parm *cur_parm;
1652 int rc;
1653 int i;
1654
1655 /* Mark all parameters as don't having an explicit value */
1656 memset(&parms[0], 0, sizeof(mon_session->cmd_parms));
1657 memcpy(&parms[0], cmd->u.cmd.parms, sizeof(bcmcli_cmd_parm)*cmd->u.cmd.num_parms);
1658 /* Clear array buffers */
1659 for (i = 0; i < cmd->u.cmd.num_parms; i++)
1660 {
1661 if (parms[i].max_array_size)
1662 {
1663 BUG_ON(!parms[i].values);
1664 memset(parms[i].values, 0, sizeof(bcmcli_parm_value) * parms[i].max_array_size);
1665 mon_session->value_status[i].values_set = bcmos_calloc(sizeof(bcmos_bool) * parms[i].max_array_size);
1666 if (!mon_session->value_status[i].values_set)
1667 {
1668 bcmcli_session_print(mon_session->session, "MON: %s> Couldn't allocate value status array for %s\n",
1669 cmd->name, pairs[i].name);
1670 return BCM_ERR_NOMEM;
1671 }
1672 }
1673 else
1674 {
1675 mon_session->value_status[i].value_set = BCMOS_FALSE;
1676 }
1677 }
1678 mon_session->curcmd = cmd;
1679 mon_session->num_parms = cmd->u.cmd.num_parms;
1680 if (last)
1681 *last = 0;
1682 /* Build a format string */
1683 for (i=0; i<npairs && pairs[i].type != BCMCLI_TOKEN_BREAK; i++)
1684 {
1685 parm_value = pairs[i].value;
1686 if (last)
1687 *last = i;
1688 cur_parm = NULL;
1689 /* Named parameter ? */
1690 if (pairs[i].name)
1691 {
1692 if ( (cmd->u.cmd.extras.flags & BCMCLI_CMD_FLAG_NO_NAME_PARMS) )
1693 {
1694 if (!suppress_err_print)
1695 {
1696 bcmcli_session_print(mon_session->session, "MON: %s> Doesn't support named parameters. %s is unexpected\n",
1697 cmd->name, pairs[i].name);
1698 }
1699 return BCM_ERR_PARM;
1700 }
1701 positional = 0; /* No more positional parameters */
1702 /* Check name */
1703 cur_parm = _bcmcli_find_named_parm(mon_session, pairs[i].name);
1704 if (!cur_parm)
1705 {
1706 if (!suppress_err_print)
1707 {
1708 bcmcli_session_print(mon_session->session, "MON: %s> parameter <%s> doesn't exist\n",
1709 cmd->name, pairs[i].name);
1710 }
1711 return BCM_ERR_PARM;
1712 }
1713 if (!parm_value)
1714 {
1715 if (!suppress_err_print)
1716 {
1717 bcmcli_session_print(mon_session->session, "MON: %s> <%s>: value is missing\n",
1718 cmd->name, cur_parm->name);
1719 }
1720 return BCM_ERR_PARM;
1721 }
1722 }
1723 else
1724 {
1725 /* it can still be named ENUM parameter (without =value). In this case the 1st
1726 * enum value is assumed. Check it
1727 */
1728 if (parm_value && (cur_parm = _bcmcli_find_named_parm(mon_session, parm_value)) &&
1729 (cur_parm->type == BCMCLI_PARM_ENUM))
1730 {
1731 pairs[i].name = parm_value;
1732 pairs[i].value = parm_value = cur_parm->enum_table->name;
1733 positional = 0; /* No more positional parameters */
1734 }
1735 else
1736 {
1737 if (!positional)
1738 {
1739 if (!suppress_err_print)
1740 bcmcli_session_print(mon_session->session, "MON: %s> Expected named parameter. Got %s\n", cmd->name, parm_value);
1741 return BCM_ERR_PARM;
1742 }
1743 cur_parm = &parms[i];
1744 }
1745 if (!cur_parm->name)
1746 {
1747 if (!suppress_err_print)
1748 bcmcli_session_print(mon_session->session, "MON: %s> Too many parameters. %s is unexpected\n", cmd->name, parm_value);
1749 return BCM_ERR_PARM;
1750 }
1751 }
1752
1753 if (cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED)
1754 {
1755 if (!suppress_err_print)
1756 {
1757 bcmcli_session_print(mon_session->session, "MON: %s> Attempt to assign parameter %s more than once\n",
1758 cmd->name, cur_parm->name);
1759 }
1760 return BCM_ERR_PARM;
1761 }
1762
1763 if (parm_value)
1764 {
1765 if (cur_parm->type == BCMCLI_PARM_STRING)
1766 cur_parm->value.string = parm_value;
1767 else
1768 {
1769 rc = _bcmcli_parse_value(mon_session, cmd, cur_parm, parm_value, suppress_err_print);
1770 if (rc)
1771 return rc;
1772
1773 /* For parameter-selector extend list of parameters accordingly */
1774 if (cur_parm->flags & BCMCLI_PARM_FLAG_SELECTOR)
1775 {
1776 rc = _bcmcli_extend_parm_table(mon_session, cur_parm, parm_value);
1777 if (rc)
1778 return rc;
1779 }
1780 }
1781 cur_parm->flags |= BCMCLI_PARM_FLAG_ASSIGNED;
1782 }
1783 }
1784 return BCM_ERR_OK;
1785}
1786
1787/* Parse p_inbuf string based on parameter descriptions in <p_token>.
1788 * Fill parameter values in <p_token>.
1789 * Returns the number of parameters filled or BCM_ERR_PARM
1790 * To Do: add a option of one-by-one user input of missing parameters.
1791 */
1792static int _bcmcli_parse_parms( bcmcli_session_extras *mon_session, bcmcli_entry *cmd, bcmcli_name_value *pairs, int npairs)
1793{
1794 bcmcli_cmd_parm *parms=mon_session->cmd_parms;
1795 int rc;
1796 int i;
1797
1798 /* Populate parameter table */
1799 rc = _bcmcli_populate_parms(mon_session, cmd, pairs, npairs, BCMOS_FALSE, NULL);
1800 if (rc)
1801 goto err_return;
1802
1803
1804 rc = BCM_ERR_PARM;
1805
1806 /* Make sure that parameters are OK. Check range, process default values */
1807 for (i=0; i<mon_session->num_parms; i++)
1808 {
1809 bcmcli_cmd_parm *cur_parm = &parms[i];
1810
1811 if (!(cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
1812 {
1813 if ((cur_parm->flags & BCMCLI_PARM_FLAG_DEFVAL))
1814 {
1815 cur_parm->flags |= BCMCLI_PARM_FLAG_ASSIGNED;
1816 }
1817 else if (!(cur_parm->flags & BCMCLI_PARM_FLAG_OPTIONAL) )
1818 {
1819 /* Mandatory parameter missing */
1820 bcmcli_session_print(mon_session->session, "MON: %s> Mandatory parameter <%s> is missing\n", cmd->name, parms[i].name);
1821 goto err_return;
1822 }
1823 }
1824
1825 /* Check value */
1826 if ((cur_parm->flags & BCMCLI_PARM_FLAG_RANGE))
1827 {
1828 if ((cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
1829 {
1830 if (cur_parm->array_size)
1831 {
1832 uint32_t j;
1833
1834 for (j = 0; j < cur_parm->array_size; j++)
1835 {
1836 if (((cur_parm->values[j].number < cur_parm->low_val) ||
1837 (cur_parm->values[j].number > cur_parm->hi_val)))
1838 {
1839 bcmcli_session_print(mon_session->session, "MON: %s> <%s>: %ld out of range (%ld, %ld)\n",
1840 cmd->name, cur_parm->name, cur_parm->values[j].number, cur_parm->low_val, cur_parm->hi_val);
1841 goto err_return;
1842 }
1843 }
1844 }
1845 else if (((cur_parm->value.number < cur_parm->low_val) ||
1846 (cur_parm->value.number > cur_parm->hi_val)))
1847 {
1848 bcmcli_session_print(mon_session->session, "MON: %s> <%s>: %ld out of range (%ld, %ld)\n",
1849 cmd->name, cur_parm->name, cur_parm->value.number, cur_parm->low_val, cur_parm->hi_val);
1850 goto err_return;
1851 }
1852 }
1853 }
1854 }
1855
1856 return BCM_ERR_OK;
1857
1858err_return:
1859 _bcmcli_free_session_value_status(mon_session);
1860 return rc;
1861}
1862
1863/* insert value skipping partial match trhat is already present */
1864static void _bcmcli_insert(const char *partial_match, const char *insert_val1,
1865 const char *insert_val2, char *insert_str, uint32_t insert_size)
1866{
1867 if (partial_match)
1868 insert_val1 += strlen(partial_match);
1869 bcmcli_strncpy(insert_str, insert_val1, insert_size);
1870 if (insert_val2)
1871 bcmcli_strncat(insert_str, insert_val2, insert_size);
1872}
1873
1874static void _bcmcli_update_longest_match(char *longest_match, const char *name)
1875{
1876 uint32_t nlen = strlen(name);
1877 uint32_t lmlen = strlen(longest_match);
1878
1879 if (nlen < lmlen)
1880 {
1881 lmlen = nlen;
1882 }
1883 while (lmlen && memcmp(longest_match, name, lmlen))
1884 {
1885 --lmlen;
1886 }
1887 longest_match[lmlen] = 0;
1888}
1889
1890
1891/* extend value.
1892 * If !enum - do nothing
1893 * If more than 1 matching value - display them
1894 * If no matching value - do nothing
1895 * If 1 matching value - insert
1896 */
1897static void _bcmcli_extend_value(bcmcli_session_extras *mon_session, bcmcli_cmd_parm *parm,
1898 const char *partial_value, char *insert_str, uint32_t insert_size)
1899{
1900 int nmatch = 0;
1901 bcmcli_enum_val *vals = parm->enum_table;
1902 char longest_match[BCMCLI_MAX_SEARCH_SUBSTR_LENGTH]="";
1903
1904 if ((parm->type != BCMCLI_PARM_ENUM && parm->type != BCMCLI_PARM_ENUM_MASK) || !vals)
1905 return;
1906
1907 /* If enum mask, partial value can be a sum of values. Skip past the last '+' sign */
1908 if (parm->type == BCMCLI_PARM_ENUM_MASK && partial_value)
1909 {
1910 char *pdel = strrchr(partial_value, BCMCLI_ENUM_MASK_DEL_CHAR);
1911 if (pdel)
1912 partial_value = pdel + 1;
1913 }
1914
1915 while (vals->name)
1916 {
1917 if (!partial_value || !strncmp(vals->name, partial_value, strlen(partial_value)))
1918 {
1919 if (!nmatch)
1920 {
1921 bcmcli_strncpy(longest_match, vals->name, sizeof(longest_match));
1922 }
1923 else
1924 {
1925 _bcmcli_update_longest_match(longest_match, vals->name);
1926 }
1927 ++nmatch;
1928 }
1929 ++vals;
1930 }
1931 if (!nmatch)
1932 return;
1933 if (nmatch == 1)
1934 {
1935 _bcmcli_insert(partial_value, longest_match, " ", insert_str, insert_size);
1936 return;
1937 }
1938 /* display all matching values */
1939 _bcmcli_insert(partial_value, longest_match, "", insert_str, insert_size);
1940 bcmcli_session_print(mon_session->session, "\n");
1941 vals = parm->enum_table;
1942 while (vals->name)
1943 {
1944 if (!partial_value || !strncmp(vals->name, partial_value, strlen(partial_value)))
1945 bcmcli_session_print(mon_session->session, " %s", vals->name);
1946 ++vals;
1947 }
1948 bcmcli_session_print(mon_session->session, "\n");
1949}
1950
1951/* calculate number of matching parameter names */
1952static int _bcmcli_num_matching_names(bcmcli_session_extras *mon_session, const char *partial_value, int *first_match)
1953{
1954 int i;
1955 int nmatch = 0;
1956
1957 *first_match = -1;
1958 for (i = 0; i < mon_session->num_parms; i++)
1959 {
1960 uint32_t flags = mon_session->cmd_parms[i].flags;
1961 if ((flags & BCMCLI_PARM_FLAG_ASSIGNED))
1962 continue;
1963 if (partial_value && strncmp(mon_session->cmd_parms[i].name, partial_value, strlen(partial_value)))
1964 continue;
1965 if (*first_match == -1)
1966 *first_match = i;
1967 ++nmatch;
1968 }
1969 return nmatch;
1970}
1971
1972/* calculate longest matching string.
1973 * returns number of matching parameters
1974 */
1975static int _bcmcli_longest_match(bcmcli_session_extras *mon_session, const char *partial_value,
1976 char *longest_match, uint32_t longest_match_size, int *first_match)
1977{
1978 int nmatch0 = _bcmcli_num_matching_names(mon_session, partial_value, first_match);
1979 int nmatch;
1980 const char *match_name;
1981
1982 if (!nmatch0)
1983 return nmatch0;
1984 match_name = mon_session->cmd_parms[*first_match].name;
1985 if (nmatch0 == 1)
1986 {
1987 bcmcli_strncpy(longest_match, match_name, longest_match_size);
1988 return nmatch0;
1989 }
1990 bcmcli_strncpy(longest_match, match_name, longest_match_size);
1991 nmatch = _bcmcli_num_matching_names(mon_session, longest_match, first_match);
1992 while (nmatch != nmatch0)
1993 {
1994 longest_match[strlen(longest_match)-1] = 0;
1995 nmatch = _bcmcli_num_matching_names(mon_session, longest_match, first_match);
1996 }
1997 return nmatch0;
1998}
1999
2000/* display/insert unset matching names
2001 * If more than 1 matching value - display them
2002 * If no matching value - do nothing
2003 * If 1 matching value - insert
2004 */
2005static void _bcmcli_extend_name(bcmcli_session_extras *mon_session, const char *partial_value,
2006 char *insert_str, uint32_t insert_size)
2007{
2008 char longest_match[BCMCLI_MAX_SEARCH_SUBSTR_LENGTH]="";
2009 int first_match;
2010 int nmatch = _bcmcli_longest_match(mon_session, partial_value, longest_match,
2011 sizeof(longest_match), &first_match);
2012
2013 if (!nmatch)
2014 return;
2015 if (!partial_value || strcmp(partial_value, longest_match))
2016 _bcmcli_insert(partial_value, longest_match, (nmatch == 1) ? "=" : "", insert_str, insert_size);
2017 else
2018 _bcmcli_help_populated_cmd(mon_session, mon_session->curcmd, partial_value, BCMOS_TRUE);
2019}
2020
2021static int _bcmcli_extend_parms( bcmcli_session_extras *mon_session, bcmcli_name_value *pairs,
2022 int npairs, bcmos_bool last_is_space, char *insert_str, uint32_t insert_size)
2023{
2024 bcmos_errno rc;
2025 int last = 0;
2026 bcmcli_cmd_parm *help_parm = NULL;
2027 int i;
2028
2029 rc = _bcmcli_populate_parms(mon_session, mon_session->curcmd, pairs, npairs, BCMOS_TRUE, &last);
2030 if (!rc)
2031 {
2032 /* So far so good */
2033 /* If there is unset mandatory parameter - insert its name.
2034 * Otherwise, display list of unset parameters
2035 */
2036 /* Find mandatory parameter that is still unassigned */
2037 for (i = 0; i < mon_session->num_parms; i++)
2038 {
2039 uint32_t flags = mon_session->cmd_parms[i].flags;
2040 if (!(flags & (BCMCLI_PARM_FLAG_OPTIONAL | BCMCLI_PARM_FLAG_DEFVAL | BCMCLI_PARM_FLAG_ASSIGNED)))
2041 {
2042 help_parm = &mon_session->cmd_parms[i];
2043 break;
2044 }
2045 }
2046 if (help_parm)
2047 {
2048 if (!last_is_space)
2049 bcmcli_strncpy(insert_str, " ", insert_size);
2050 bcmcli_strncat(insert_str, help_parm->name, insert_size);
2051 bcmcli_strncat(insert_str, "=", insert_size);
2052 }
2053 else if (last < mon_session->num_parms)
2054 _bcmcli_help_populated_cmd(mon_session, mon_session->curcmd, NULL, BCMOS_TRUE);
2055 }
2056 else
2057 {
2058 /* Parsing failed. See what stopped at */
2059 if (last < mon_session->num_parms)
2060 {
2061 bcmcli_name_value *last_pair;
2062
2063 last_pair = &pairs[last];
2064 if (last_pair->name)
2065 {
2066 /* Try to identify by name */
2067 help_parm = _bcmcli_find_named_parm(mon_session, last_pair->name ? last_pair->name : last_pair->value);
2068 }
2069 if (help_parm)
2070 {
2071 /* Looking for values */
2072 _bcmcli_extend_value(mon_session, help_parm, last_pair->value, insert_str, insert_size);
2073 }
2074 else
2075 {
2076 /* Looking for partial name */
2077 _bcmcli_extend_name(mon_session, last_pair->name ? last_pair->name : last_pair->value,
2078 insert_str, insert_size);
2079 }
2080 }
2081 }
2082 _bcmcli_free_session_value_status(mon_session);
2083
2084 return BCM_ERR_OK;
2085}
2086
2087/* Identify token in the given directory */
2088static bcmcli_entry *_bcmcli_search_token1( bcmcli_entry *p_dir, const char **p_name, int name_len )
2089{
2090 bcmcli_entry *p_token = NULL;
2091 const char *name = *p_name;
2092 bcmcli_token_type type=_bcmcli_analyze_token(name);
2093
2094 /* Name can be qualified */
2095 if (type == BCMCLI_TOKEN_VALUE && !strncmp(name, BCMCLI_UP_STR, name_len))
2096 type = BCMCLI_TOKEN_UP;
2097
2098 switch(type)
2099 {
2100 case BCMCLI_TOKEN_ROOT:
2101 p_token = _bcmcli_root_dir;
2102 *p_name = name + strlen(BCMCLI_ROOT_STR);
2103 break;
2104 case BCMCLI_TOKEN_UP:
2105 if (p_dir->parent)
2106 p_token = p_dir->parent;
2107 else
2108 p_token = p_dir;
2109 *p_name = name + strlen(BCMCLI_UP_STR) + 1;
2110 break;
2111 case BCMCLI_TOKEN_NAME:
2112 case BCMCLI_TOKEN_VALUE:
2113 /* Check alias */
2114 p_token = p_dir->u.dir.first;
2115 while ( p_token )
2116 {
2117 if (p_token->alias &&
2118 (name_len == p_token->alias_len) &&
2119 !_bcmcli_stricmp(p_token->alias, name, p_token->alias_len))
2120 break;
2121 p_token = p_token->next;
2122 }
2123 if (!p_token)
2124 {
2125 bcmcli_entry *partial_match = NULL;
2126 /* Check name */
2127 p_token = p_dir->u.dir.first;
2128 while( p_token )
2129 {
2130 if (!_bcmcli_stricmp(p_token->name, name, name_len))
2131 {
2132 if (name_len == strlen(p_token->name))
2133 break;
2134 if (!partial_match)
2135 partial_match = p_token;
2136 }
2137 p_token = p_token->next;
2138 }
2139 if (!p_token)
2140 p_token = partial_match;
2141 }
2142 *p_name = name + name_len + 1;
2143 break;
2144 default:
2145 break;
2146 }
2147
2148 return p_token;
2149}
2150
2151
2152/* Search a token by name in the current directory.
2153 * The name can be qualified (contain path)
2154 */
2155static bcmcli_entry *_bcmcli_search_token( bcmcli_entry *p_dir, const char *name )
2156{
2157 bcmcli_entry *p_token;
2158 const char *name0 = name;
2159 const char *p_slash;
2160
2161 if (!name[0])
2162 return p_dir;
2163
2164 /* Check if name is qualified */
2165 do
2166 {
2167 p_slash = strchr(name, '/');
2168 if (p_slash)
2169 {
2170 if (p_slash == name0)
2171 {
2172 p_dir = p_token = _bcmcli_root_dir;
2173 name = p_slash + 1;
2174 }
2175 else
2176 {
2177 p_token = _bcmcli_search_token1(p_dir, &name, p_slash - name);
2178 if (p_token && (p_token->sel == BCMCLI_ENTRY_DIR))
2179 p_dir = p_token;
2180 }
2181 }
2182 else
2183 {
2184 p_token = _bcmcli_search_token1(p_dir, &name, strlen(name));
2185 }
2186 } while (p_slash && p_token && *name);
2187
2188 return p_token;
2189}
2190
2191
2192
2193/* Display help for each entry in the current directory */
2194static void _bcmcli_help_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir)
2195{
2196 bcmcli_session *help_session = _bcmcli_help_session_open(mon_session->session);
2197 bcmcli_entry *p_token;
2198 char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH];
2199
2200 _bcmcli_qualified_name(p_dir, buffer, sizeof(buffer));
2201 bcmcli_session_print(help_session, "Directory %s/ - %s\n", buffer, p_dir->help);
2202 bcmcli_session_print(help_session, "Commands:\n");
2203
2204 p_token = p_dir->u.dir.first;
2205 while ( p_token )
2206 {
2207 if (bcmcli_session_access_right(help_session) >= p_token->access_right)
2208 {
2209 if (p_token->sel == BCMCLI_ENTRY_DIR)
2210 bcmcli_session_print(help_session, "\t%s/: %s directory\n", p_token->name, p_token->help );
2211 else
2212 {
2213 char *peol = strchr(p_token->help, '\n');
2214 int help_len = peol ? peol - p_token->help : (int)strlen(p_token->help);
2215 bcmcli_session_print(help_session, "\t%s(%d parms): %.*s\n",
2216 p_token->name, p_token->u.cmd.num_parms, help_len, p_token->help );
2217 }
2218 }
2219 p_token = p_token->next;
2220 }
2221 bcmcli_session_print(help_session, "Type ? <name> for command help, \"/\"-root, \"..\"-upper\n" );
2222 _bcmcli_help_session_print_and_close(mon_session->session, help_session);
2223}
2224
2225
2226/* Display help a token */
2227static void _bcmcli_help_populated_cmd(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
2228 const char *partial_match, bcmos_bool suppress_assigned)
2229{
2230 char tmp[80];
2231 char bra, ket;
2232 uint16_t i;
2233
2234 if (suppress_assigned)
2235 bcmcli_session_print(mon_session->session, "\n");
2236 for ( i=0; i<mon_session->num_parms; i++ )
2237 {
2238 bcmcli_cmd_parm *cur_parm = &mon_session->cmd_parms[i];
2239 if (suppress_assigned && (cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
2240 continue;
2241 if (partial_match && memcmp(partial_match, cur_parm->name, strlen(partial_match)))
2242 continue;
2243
2244 if ((cur_parm->flags & BCMCLI_PARM_FLAG_OPTIONAL))
2245 {
2246 bra = '[';
2247 ket=']';
2248 }
2249 else
2250 {
2251 bra = '<';
2252 ket='>';
2253 }
2254 bcmcli_session_print(mon_session->session, "\t%c%s(%s)", bra, cur_parm->name, _bcmcli_get_type_name(cur_parm) );
2255 if (cur_parm->max_array_size || cur_parm->type == BCMCLI_PARM_BUFFER)
2256 {
2257 uint32_t num_entries = (cur_parm->type == BCMCLI_PARM_BUFFER) ? cur_parm->value.buffer.len : cur_parm->max_array_size;
2258 bcmcli_session_print(mon_session->session, "[%u]", num_entries);
2259 }
2260 if (cur_parm->type == BCMCLI_PARM_ENUM || cur_parm->type == BCMCLI_PARM_ENUM_MASK)
2261 {
2262 bcmcli_enum_val *values=cur_parm->enum_table;
2263 bcmcli_session_print(mon_session->session, " {");
2264 while (values->name)
2265 {
2266 if (values!=cur_parm->enum_table)
2267 bcmcli_session_print(mon_session->session, ", ");
2268 bcmcli_session_print(mon_session->session, "%s", values->name);
2269 ++values;
2270 }
2271 bcmcli_session_print(mon_session->session, "}");
2272 }
2273 if ((cur_parm->flags & BCMCLI_PARM_FLAG_DEFVAL))
2274 {
2275 bcmcli_session_print(mon_session->session, "=");
2276 cur_parm->format_cb(cur_parm, cur_parm->value, tmp, sizeof(tmp));
2277 bcmcli_session_print(mon_session->session, "%s", tmp);
2278 }
2279 if ((cur_parm->flags & BCMCLI_PARM_FLAG_RANGE))
2280 {
2281 bcmcli_parm_value low_val = { .number = cur_parm->low_val };
2282 bcmcli_parm_value hi_val = { .number = cur_parm->hi_val };
2283
2284 bcmcli_session_print(mon_session->session, " (");
2285 cur_parm->format_cb(cur_parm, low_val, tmp, sizeof(tmp));
2286 bcmcli_session_print(mon_session->session, "%s..", tmp);
2287 cur_parm->format_cb(cur_parm, hi_val, tmp, sizeof(tmp));
2288 bcmcli_session_print(mon_session->session, "%s)", tmp);
2289 }
2290 bcmcli_session_print(mon_session->session, "%c ", ket);
2291 bcmcli_session_print(mon_session->session, "- %s\n", cur_parm->description);
2292 }
2293
2294 /* Print extra help if command has unresolved selector */
2295 if (mon_session->num_parms &&
2296 (mon_session->cmd_parms[mon_session->num_parms-1].flags & BCMCLI_PARM_FLAG_SELECTOR) &&
2297 !(mon_session->cmd_parms[mon_session->num_parms-1].flags & BCMCLI_PARM_FLAG_ASSIGNED))
2298 {
2299 const char *sel_name = mon_session->cmd_parms[mon_session->num_parms-1].name;
2300 bcmcli_session_print(mon_session->session, "Add %s=%s_value to see %s-specific parameters\n",
2301 sel_name, sel_name, sel_name);
2302 }
2303 bcmcli_session_print(mon_session->session, "\n");
2304}
2305
2306
2307/* Display help a token */
2308static void _bcmcli_help_entry(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
2309 bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print)
2310{
2311 char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH];
2312
2313 if (p_token->sel == BCMCLI_ENTRY_DIR)
2314 {
2315 _bcmcli_help_dir(mon_session, p_token);
2316 return;
2317 }
2318
2319 /* Populate parameter table */
2320 _bcmcli_populate_parms(mon_session, p_token, pairs, npairs, suppress_err_print, NULL);
2321
2322 _bcmcli_qualified_name(p_token, buffer, sizeof(buffer));
2323 bcmcli_session_print(mon_session->session, "%s: \t%s\n", buffer, p_token->help );
2324 if (p_token->u.cmd.num_parms)
2325 bcmcli_session_print(mon_session->session, "Parameters:\n");
2326 _bcmcli_help_populated_cmd(mon_session, p_token, NULL, BCMOS_FALSE);
2327 _bcmcli_free_session_value_status(mon_session);
2328}
2329
2330
2331/* Choose unique alias for <name> in <p_dir> */
2332/* Currently only single-character aliases are supported */
2333static void __bcmcli_chooseAlias(bcmcli_entry *p_dir, bcmcli_entry *p_new_token, int from)
2334{
2335 bcmcli_entry *p_token;
2336 int i;
2337 char c;
2338
2339 _bcmcli_strlwr( p_new_token->name );
2340 i = from;
2341 while ( p_new_token->name[i] )
2342 {
2343 c = p_new_token->name[i];
2344 p_token = p_dir->u.dir.first;
2345
2346 while ( p_token )
2347 {
2348 if (p_token->alias &&
2349 (tolower( *p_token->alias ) == c) )
2350 break;
2351 if (strlen(p_token->name)<=2 && tolower(p_token->name[0])==c)
2352 break;
2353 p_token = p_token->next;
2354 }
2355 if (p_token)
2356 ++i;
2357 else
2358 {
2359 p_new_token->name[i] = toupper( c );
2360 p_new_token->alias = &p_new_token->name[i];
2361 p_new_token->alias_len = 1;
2362 break;
2363 }
2364 }
2365}
2366
2367/* isupper wrapper */
2368static inline int _bcmcli_isupper(char c)
2369{
2370 return isupper((int)c);
2371}
2372
2373static void _bcmcli_choose_alias(bcmcli_entry *p_dir, bcmcli_entry *p_new_token)
2374{
2375 int i=0;
2376 p_new_token->alias_len = 0;
2377 p_new_token->alias = NULL;
2378 /* Don't try to alias something short */
2379 if (strlen(p_new_token->name) < BCMCLI_MIN_NAME_LENGTH_FOR_ALIAS)
2380 return;
2381 /* Try pre-set alias 1st */
2382 while ( p_new_token->name[i] )
2383 {
2384 if (_bcmcli_isupper(p_new_token->name[i]))
2385 break;
2386 i++;
2387 }
2388 if (p_new_token->name[i])
2389 __bcmcli_chooseAlias(p_dir, p_new_token, i);
2390 if (p_new_token->alias != &p_new_token->name[i])
2391 __bcmcli_chooseAlias(p_dir, p_new_token, 0);
2392}
2393
2394
2395/* Convert string s to lower case. Return pointer to s */
2396static char * _bcmcli_strlwr( char *s )
2397{
2398 char *s0=s;
2399
2400 while ( *s )
2401 {
2402 *s = tolower( *s );
2403 ++s;
2404 }
2405
2406 return s0;
2407}
2408
2409
2410/* Compare strings case incensitive */
2411static int _bcmcli_stricmp(const char *s1, const char *s2, int len)
2412{
2413 int i;
2414
2415 for ( i=0; (i<len || len<0); i++ )
2416 {
2417 if (tolower( s1[i]) != tolower( s2[i] ))
2418 return 1;
2419 if (!s1[i])
2420 break;
2421 }
2422
2423 return 0;
2424}
2425
2426static const char *_bcmcli_get_type_name(const bcmcli_cmd_parm *parm)
2427{
2428 bcmcli_parm_type type = parm->type;
2429 static const char *type_name[] = {
2430 [BCMCLI_PARM_DECIMAL] = "decimal",
2431 [BCMCLI_PARM_DECIMAL64] = "decimal64",
2432 [BCMCLI_PARM_UDECIMAL] = "udecimal",
2433 [BCMCLI_PARM_UDECIMAL64] = "udecimal64",
2434 [BCMCLI_PARM_HEX] = "hex",
2435 [BCMCLI_PARM_HEX64] = "hex64",
2436 [BCMCLI_PARM_NUMBER] = "number",
2437 [BCMCLI_PARM_NUMBER64] = "number64",
2438 [BCMCLI_PARM_UNUMBER] = "unumber",
2439 [BCMCLI_PARM_UNUMBER64] = "unumber64",
2440 [BCMCLI_PARM_FLOAT] = "float",
2441 [BCMCLI_PARM_DOUBLE] = "double",
2442 [BCMCLI_PARM_ENUM] = "enum",
2443 [BCMCLI_PARM_ENUM_MASK] = "enum_mask",
2444 [BCMCLI_PARM_STRING] = "string",
2445 [BCMCLI_PARM_IP] = "IP",
2446 [BCMCLI_PARM_IPV6] = "IPv6",
2447 [BCMCLI_PARM_MAC] = "MAC",
2448 [BCMCLI_PARM_BUFFER] = "buffer",
2449 [BCMCLI_PARM_USERDEF] = "userdef",
2450 };
2451 static const char *undefined = "undefined";
2452 static const char *selector = "selector";
2453 if (type > BCMCLI_PARM_USERDEF || !type_name[type])
2454 return undefined;
2455 if (type == BCMCLI_PARM_ENUM && (parm->flags & BCMCLI_PARM_FLAG_SELECTOR))
2456 return selector;
2457 return type_name[type];
2458}
2459
2460/* Assign default callbacks */
2461static void _bcmcli_assign_callbacks(bcmcli_cmd_parm *parm)
2462{
2463 if (parm->type == BCMCLI_PARM_ENUM)
2464 {
2465 parm->scan_cb = _bcmcli_enum_scan_cb;
2466 parm->format_cb = _bcmcli_enum_format_cb;
2467 }
2468 else if (parm->type == BCMCLI_PARM_ENUM_MASK)
2469 {
2470 parm->scan_cb = _bcmcli_enum_mask_scan_cb;
2471 parm->format_cb = _bcmcli_enum_mask_format_cb;
2472 }
2473 else if (parm->type == BCMCLI_PARM_BUFFER)
2474 {
2475 if (!parm->scan_cb)
2476 parm->scan_cb = _bcmcli_buffer_scan_cb;
2477 if (!parm->format_cb)
2478 parm->format_cb = _bcmcli_dft_format_cb;
2479 }
2480 else
2481 {
2482 if (!parm->scan_cb)
2483 parm->scan_cb = _bcmcli_dft_scan_cb;
2484 if (!parm->format_cb)
2485 parm->format_cb = _bcmcli_dft_format_cb;
2486 }
2487}
2488
2489
2490/* Convert hex-string to binary data.
2491 * Returns: converted length >=0 or error < 0
2492 */
2493static int _bcmcli_strhex(const char *src, uint8_t *dst, uint16_t dst_len)
2494{
2495 uint16_t src_len = (uint16_t)strlen( src );
2496 uint16_t i = src_len, j, shift = 0;
2497
2498 if ( !dst || !dst_len || (src_len > 2*dst_len) || (src_len%2) )
2499 {
2500 return BCM_ERR_PARM;
2501 }
2502
2503 /* Calculate hex buffer length and fill it up from right-to-left
2504 * in order to start the process from LS nibble
2505 */
2506 dst_len = src_len / 2;
2507 memset(dst, 0, dst_len);
2508 j = dst_len-1;
2509 do
2510 {
2511 int c = src[--i];
2512
2513 if ( (c>='0') && (c<='9') )
2514 {
2515 c = c - '0';
2516 }
2517 else if ( (c>='a') && (c<='f') )
2518 {
2519 c = 0xA + c - 'a';
2520 }
2521 else if ( (c>='A') && (c<='F') )
2522 {
2523 c = 0xA + c - 'A';
2524 }
2525 else
2526 {
2527 return BCM_ERR_PARM;
2528 }
2529
2530 dst[j] |= (uint8_t)(c<<shift); /* shift can have 1 of 2 values: 0 and 4 */
2531
2532 j -= shift>>2; /* move to the next byte if we've just filled the ms nibble */
2533 shift ^= 4; /* alternate nibbles */
2534
2535 } while ( i );
2536
2537 return dst_len;
2538}
2539
2540/* Default function for string->value conversion.
2541 * Returns 0 if OK
2542 */
2543static bcmos_errno _bcmcli_dft_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
2544{
2545 char *p_end = NULL;
2546 int n;
2547
2548 if (parm->type == BCMCLI_PARM_UDECIMAL ||
2549 parm->type == BCMCLI_PARM_UDECIMAL64 ||
2550 parm->type == BCMCLI_PARM_UNUMBER ||
2551 parm->type == BCMCLI_PARM_UNUMBER64)
2552 {
2553 /* strtoul returns OK even when parsing a negative number */
2554 if (string_val[0] == '-')
2555 {
2556 return BCM_ERR_PARM;
2557 }
2558 }
2559
2560 switch(parm->type)
2561 {
2562 case BCMCLI_PARM_DECIMAL:
2563 value->number = strtol(string_val, &p_end, 10);
2564 break;
2565 case BCMCLI_PARM_UDECIMAL:
2566 value->unumber = strtoul(string_val, &p_end, 10);
2567 break;
2568 case BCMCLI_PARM_DECIMAL64:
2569 value->number64 = strtoll(string_val, &p_end, 10);
2570 break;
2571 case BCMCLI_PARM_UDECIMAL64:
2572 value->unumber64 = strtoull(string_val, &p_end, 10);
2573 break;
2574 case BCMCLI_PARM_HEX:
2575 value->unumber = strtoul(string_val, &p_end, 16);
2576 break;
2577 case BCMCLI_PARM_HEX64:
2578 value->unumber64 = strtoull(string_val, &p_end, 16);
2579 break;
2580 case BCMCLI_PARM_NUMBER:
2581 value->number = strtol(string_val, &p_end, 0);
2582 break;
2583 case BCMCLI_PARM_UNUMBER:
2584 value->unumber = strtoul(string_val, &p_end, 0);
2585 break;
2586 case BCMCLI_PARM_NUMBER64:
2587 value->number64 = strtoll(string_val, &p_end, 0);
2588 break;
2589 case BCMCLI_PARM_UNUMBER64:
2590 value->unumber64 = strtoull(string_val, &p_end, 0);
2591 break;
2592 case BCMCLI_PARM_FLOAT:
2593 case BCMCLI_PARM_DOUBLE:
2594 value->d = strtod(string_val, &p_end);
2595 break;
2596 case BCMCLI_PARM_MAC:
2597 {
2598 unsigned m0, m1, m2, m3, m4, m5;
2599 n = sscanf(string_val, "%02x:%02x:%02x:%02x:%02x:%02x",
2600 &m0, &m1, &m2, &m3, &m4, &m5);
2601 if (n != 6)
2602 {
2603 n = sscanf(string_val, "%02x%02x%02x%02x%02x%02x",
2604 &m0, &m1, &m2, &m3, &m4, &m5);
2605 }
2606 if (n != 6)
2607 return BCM_ERR_PARM;
2608 if (m0 > 255 || m1 > 255 || m2 > 255 || m3 > 255 || m4 > 255 || m5 > 255)
2609 return BCM_ERR_PARM;
2610 value->mac.u8[0] = m0;
2611 value->mac.u8[1] = m1;
2612 value->mac.u8[2] = m2;
2613 value->mac.u8[3] = m3;
2614 value->mac.u8[4] = m4;
2615 value->mac.u8[5] = m5;
2616 break;
2617 }
2618 case BCMCLI_PARM_IP:
2619 {
2620 int n1, n2, n3, n4;
2621 n = sscanf(string_val, "%d.%d.%d.%d", &n1, &n2, &n3, &n4);
2622 if (n != 4)
2623 return BCM_ERR_PARM;
2624 if ((unsigned)n1 > 255 || (unsigned)n2 > 255 || (unsigned)n3 > 255 || (unsigned)n4 > 255)
2625 return BCM_ERR_PARM;
2626 value->unumber = (n1 << 24) | (n2 << 16) | (n3 << 8) | n4;
2627 break;
2628 }
2629
2630 default:
2631 return BCM_ERR_PARM;
2632 }
2633 if (p_end && *p_end)
2634 return BCM_ERR_PARM;
2635 return BCM_ERR_OK;
2636}
2637
2638static void _bcmcli_dft_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size)
2639{
2640 switch(parm->type)
2641 {
2642 case BCMCLI_PARM_DECIMAL:
2643 snprintf(buffer, size, "%ld", value.number);
2644 break;
2645 case BCMCLI_PARM_UDECIMAL:
2646 snprintf(buffer, size, "%lu", value.unumber);
2647 break;
2648 case BCMCLI_PARM_DECIMAL64:
2649 snprintf(buffer, size, "%lld", value.number64);
2650 break;
2651 case BCMCLI_PARM_UDECIMAL64:
2652 snprintf(buffer, size, "%llu", value.unumber64);
2653 break;
2654 case BCMCLI_PARM_HEX:
2655 snprintf(buffer, size, "0x%lx", value.unumber);
2656 break;
2657 case BCMCLI_PARM_HEX64:
2658 snprintf(buffer, size, "0x%llx", value.unumber64);
2659 break;
2660 case BCMCLI_PARM_NUMBER:
2661 snprintf(buffer, size, "%ld", value.number);
2662 break;
2663 case BCMCLI_PARM_NUMBER64:
2664 snprintf(buffer, size, "%lld", value.number64);
2665 break;
2666 case BCMCLI_PARM_UNUMBER:
2667 snprintf(buffer, size, "%lu", value.unumber);
2668 break;
2669 case BCMCLI_PARM_UNUMBER64:
2670 snprintf(buffer, size, "%llu", value.unumber64);
2671 break;
2672 case BCMCLI_PARM_FLOAT:
2673 case BCMCLI_PARM_DOUBLE:
2674 snprintf(buffer, size, "%f", value.d);
2675 break;
2676 case BCMCLI_PARM_STRING:
2677 snprintf(buffer, size, "%s", value.string);
2678 break;
2679 case BCMCLI_PARM_MAC:
2680 snprintf(buffer, size, "%02x:%02x:%02x:%02x:%02x:%02x",
2681 parm->value.mac.u8[0], parm->value.mac.u8[1], parm->value.mac.u8[2],
2682 parm->value.mac.u8[3], parm->value.mac.u8[4], parm->value.mac.u8[5]);
2683 break;
2684 case BCMCLI_PARM_IP:
2685 snprintf(buffer, size, "%d.%d.%d.%d",
2686 (int)((parm->value.unumber >> 24) & 0xff), (int)((parm->value.unumber >> 16) & 0xff),
2687 (int)((parm->value.unumber >> 8) & 0xff), (int)(parm->value.unumber & 0xff));
2688 break;
2689
2690 default:
2691 bcmcli_strncpy(buffer, "*unknown*", size);
2692 }
2693}
2694
2695static bcmos_errno _bcmcli_enum_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
2696{
2697 bcmcli_enum_val *values=parm->enum_table;
2698 while (values->name)
2699 {
2700 if (!_bcmcli_stricmp(values->name, string_val, -1))
2701 {
2702 value->enum_val = values->val;
2703 return BCM_ERR_OK;
2704 }
2705 ++values;
2706 }
2707 return BCM_ERR_PARM;
2708}
2709
2710static void _bcmcli_enum_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size)
2711{
2712 bcmcli_enum_val *values=parm->enum_table;
2713 while (values->name)
2714 {
2715 if (values->val == value.enum_val)
2716 break;
2717 ++values;
2718 }
2719 if (values->name)
2720 strncpy(buffer, values->name, size);
2721 else
2722 strncpy(buffer, "*invalid*", size);
2723}
2724
2725static bcmos_errno _bcmcli_enum_mask_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
2726{
2727 bcmcli_parm_value val1;
2728 char *del;
2729 bcmos_errno err;
2730
2731 value->number = 0;
2732
2733 /* string_val is a combination of enum values separated by BCMCLI_ENUM_MASK_DEL_STR */
2734 del = strchr(string_val, BCMCLI_ENUM_MASK_DEL_CHAR);
2735 while (del)
2736 {
2737 char single_val[64];
2738 if (del - string_val >= sizeof(single_val))
2739 return BCM_ERR_OVERFLOW;
2740 memcpy(single_val, string_val, del - string_val);
2741 single_val[del - string_val] = 0;
2742 err = _bcmcli_enum_scan_cb(parm, &val1, single_val);
2743 if (err)
2744 return err;
2745 value->enum_val |= val1.enum_val;
2746 string_val = del+1;
2747 del = strchr(string_val, BCMCLI_ENUM_MASK_DEL_CHAR);
2748 }
2749 err = _bcmcli_enum_scan_cb(parm, &val1, string_val);
2750 if (err)
2751 return err;
2752 value->number |= val1.enum_val;
2753 return BCM_ERR_OK;
2754}
2755
2756static void _bcmcli_enum_mask_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size)
2757{
2758 bcmcli_enum_val *values=parm->enum_table;
2759 const char *none = NULL;
2760 *buffer = 0;
2761 while (values->name)
2762 {
2763 if (values->val == 0)
2764 {
2765 none = values->name;
2766 }
2767 if ((values->val & value.enum_val) != 0)
2768 {
2769 if (*buffer)
2770 strncat(buffer, BCMCLI_ENUM_MASK_DEL_STR, size - strlen(buffer));
2771 strncat(buffer, values->name, size - strlen(buffer));
2772 }
2773 ++values;
2774 }
2775 if (! *buffer)
2776 strncpy(buffer, NULL != none ? none : "0", size);
2777}
2778
2779static bcmos_errno _bcmcli_buffer_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
2780{
2781 int n;
2782
2783 if (!value->buffer.start)
2784 return BCM_ERR_PARM;
2785 value->buffer.curr = value->buffer.start;
2786 if (strcmp(string_val, "-") == 0)
2787 {
2788 return BCM_ERR_OK;
2789 }
2790 n = _bcmcli_strhex(string_val, value->buffer.start, value->buffer.len);
2791 if (n < 0)
2792 return n;
2793 bcmolt_buf_skip(&value->buffer, n);
2794
2795 return BCM_ERR_OK;
2796}
2797
2798static const char *_bcmcli_qualified_name(bcmcli_entry *token, char *buffer, int size )
2799{
2800 bcmcli_entry *parent = token->parent;
2801 char qual_name[BCMCLI_MAX_QUAL_NAME_LENGTH];
2802 *buffer=0;
2803 while (parent)
2804 {
2805 bcmcli_strncpy(qual_name, parent->name, sizeof(qual_name));
2806 if (parent->parent)
2807 bcmcli_strncat(qual_name, "/", sizeof(qual_name));
2808 bcmcli_strncat(qual_name, buffer, sizeof(qual_name));
2809 bcmcli_strncpy(buffer, qual_name, size);
2810 parent = parent->parent;
2811 }
2812 size -= strlen(buffer);
2813 bcmcli_strncat(buffer, token->name, size);
2814 return buffer;
2815}
2816
2817/*
2818 * Exports
2819 */
2820EXPORT_SYMBOL(bcmcli_dir_add);
2821EXPORT_SYMBOL(bcmcli_dir_find);
2822EXPORT_SYMBOL(bcmcli_token_name);
2823EXPORT_SYMBOL(bcmcli_cmd_add);
2824EXPORT_SYMBOL(bcmcli_session_open);
2825EXPORT_SYMBOL(bcmcli_session_close);
2826EXPORT_SYMBOL(bcmcli_parse);
2827EXPORT_SYMBOL(bcmcli_stop);
2828EXPORT_SYMBOL(bcmcli_is_stopped);
2829EXPORT_SYMBOL(bcmcli_dir_get);
2830EXPORT_SYMBOL(bcmcli_dir_set);
2831EXPORT_SYMBOL(bcmcli_parm_number);
2832EXPORT_SYMBOL(bcmcli_parm_is_set);
2833EXPORT_SYMBOL(bcmcli_enum_parm_stringval);
2834EXPORT_SYMBOL(bcmcli_token_destroy);
2835EXPORT_SYMBOL(bcmcli_enum_bool_table);