1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
4 * UI Command Dispatch File: ui_command.c
6 * This module contains routines to maintain the command table,
7 * parse and execute commands
9 * Author: Mitch Lichtenberg (mpl@broadcom.com)
11 *********************************************************************
13 * Copyright 2000,2001,2002,2003
14 * Broadcom Corporation. All rights reserved.
16 * This software is furnished under license and may be used and
17 * copied only in accordance with the following terms and
18 * conditions. Subject to these conditions, you may download,
19 * copy, install, use, modify and distribute modified or unmodified
20 * copies of this software in source and/or binary form. No title
21 * or ownership is transferred hereby.
23 * 1) Any source code used, modified or distributed must reproduce
24 * and retain this copyright notice and list of conditions
25 * as they appear in the source file.
27 * 2) No right is granted to use any trade name, trademark, or
28 * logo of Broadcom Corporation. The "Broadcom Corporation"
29 * name may not be used to endorse or promote products derived
30 * from this software without the prior written permission of
31 * Broadcom Corporation.
33 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
34 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
35 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
37 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
38 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
39 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
41 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
43 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
45 * THE POSSIBILITY OF SUCH DAMAGE.
46 ********************************************************************* */
51 #include "lib_queue.h"
53 #include "lib_types.h"
54 #include "lib_string.h"
55 #include "lib_malloc.h"
56 #include "lib_printf.h"
59 #include "cfe_device.h"
60 #include "cfe_console.h"
61 #include "cfe_error.h"
65 #include "ui_command.h"
69 typedef struct cmdtab_s
{
70 struct cmdtab_s
*sibling
;
71 struct cmdtab_s
*child
;
73 int (*func
)(ui_cmdline_t
*,int argc
,char *argv
[]);
83 #define myisalpha(x) (((x)>='A')&&((x)<='Z')&&((x)>='a')&&((x)<='z'))
84 #define myisdigit(x) (((x)>='0')&&((x)<='9'))
85 #define myisquote(x) (((x)=='\'')||((x)=='"'))
87 char *varchars
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
88 "abcdefghijklmnopqrstuvwxyz"
91 static char *tokenbreaks
= " =\t\n\'\"&|;";
92 static char *spacechars
= " \t";
94 static char *cmd_eat_quoted_arg(queue_t
*head
,ui_token_t
*t
);
97 static inline int is_white_space(ui_token_t
*t
)
99 return (strchr(spacechars
,t
->token
) != NULL
);
102 int cmd_sw_value(ui_cmdline_t
*cmd
,char *swname
,char **swvalue
)
106 for (idx
= 0; idx
< cmd
->swc
; idx
++) {
107 if (strcmp(swname
,cmd
->swv
[idx
].swname
) == 0) {
108 *swvalue
= cmd
->swv
[idx
].swvalue
;
116 int cmd_sw_posn(ui_cmdline_t
*cmd
,char *swname
)
120 for (idx
= 0; idx
< cmd
->swc
; idx
++) {
121 if (strcmp(swname
,cmd
->swv
[idx
].swname
) == 0) {
122 return cmd
->swv
[idx
].swidx
;
129 char *cmd_sw_name(ui_cmdline_t
*cmd
,int swidx
)
131 if ((swidx
< 0) || (swidx
>= cmd
->swc
)) return NULL
;
133 return cmd
->swv
[swidx
].swname
;
137 int cmd_sw_isset(ui_cmdline_t
*cmd
,char *swname
)
141 for (idx
= 0; idx
< cmd
->swc
; idx
++) {
142 if (strcmp(swname
,cmd
->swv
[idx
].swname
) == 0) {
150 char *cmd_getarg(ui_cmdline_t
*cmd
,int argnum
)
152 argnum
+= cmd
->argidx
;
153 if ((argnum
< 0) || (argnum
>= cmd
->argc
)) return NULL
;
154 return cmd
->argv
[argnum
];
157 void cmd_free(ui_cmdline_t
*cmd
)
161 for (idx
= 0; idx
< cmd
->argc
; idx
++) {
162 KFREE(cmd
->argv
[idx
]);
165 for (idx
= 0; idx
< cmd
->swc
; idx
++) {
166 KFREE(cmd
->swv
[idx
].swname
);
173 int cmd_sw_validate(ui_cmdline_t
*cmd
,char *validstr
)
183 if (cmd
->swc
== 0) return -1;
185 vdup
= strdup(validstr
);
187 for (idx
= 0; idx
< cmd
->swc
; idx
++) {
196 * Eat the next switch description from the valid string
198 x
= strchr(vptr
,'|');
208 * Get the expected arg type, if any
210 x
= strchr(vptr
,'=');
216 if ((x
= strchr(vptr
,';'))) *x
= 0;
221 if (strcmp(vptr
,cmd
->swv
[idx
].swname
) == 0) {
222 /* Value not needed and not supplied */
223 if ((atype
== 0) && (cmd
->swv
[idx
].swvalue
== NULL
)) {
226 /* value needed and supplied */
227 if ((atype
!= 0) && (cmd
->swv
[idx
].swvalue
!= NULL
)) {
230 strcpy(vdup
,validstr
);
238 strcpy(vdup
,validstr
);
243 * If not valid, return index of bad switch
254 * Return -1 if everything went well. A little strange,
255 * but it's easier this way.
262 static cmdtab_t
*cmd_findword(cmdtab_t
*list
,char *cmdword
)
265 if (strcmp(cmdword
,list
->cmdword
) == 0) return list
;
266 list
= list
->sibling
;
273 void cmd_build_cmdline(queue_t
*head
, ui_cmdline_t
*cmd
)
278 memset(cmd
, 0, sizeof(ui_cmdline_t
));
280 t
= (ui_token_t
*) q_deqnext(head
);
283 if (is_white_space(t
)) {
286 else if (t
->token
!= '-') {
287 if(cmd
->argc
< MAX_TOKENS
){
288 cmd
->argv
[cmd
->argc
] = cmd_eat_quoted_arg(head
,t
);
291 /* Token is a switch */
294 if (cmd
->swc
< MAX_SWITCHES
) {
295 cmd
->swv
[cmd
->swc
].swname
= lib_strdup(&(t
->token
));
297 if (t
->qb
.q_next
!= head
) { /* more tokens */
298 next
= (ui_token_t
*) t
->qb
.q_next
;
299 if (next
->token
== '=') { /* switch has value */
300 KFREE(t
); /* Free switch name */
301 t
= (ui_token_t
*) q_deqnext(head
); /* eat equal sign */
302 KFREE(t
); /* and free it */
303 t
= (ui_token_t
*) q_deqnext(head
); /* now have value */
305 cmd
->swv
[cmd
->swc
].swvalue
= cmd_eat_quoted_arg(head
,t
);
308 else { /* no value */
309 cmd
->swv
[cmd
->swc
].swvalue
= NULL
;
313 * swidx is the index of the argument that this
314 * switch precedes. So, if you have "foo -d bar",
315 * swidx for "-d" would be 1.
317 cmd
->swv
[cmd
->swc
].swidx
= cmd
->argc
;
322 t
= (ui_token_t
*) q_deqnext(head
);
328 int cmd_addcmd(char *command
,
329 int (*func
)(ui_cmdline_t
*,int argc
,char *argv
[]),
335 cmdtab_t
**list
= &cmd_root
;
336 cmdtab_t
*cmd
= NULL
;
341 cmd_build_list(&tokens
,command
);
344 while (cur
!= &tokens
) {
345 t
= (ui_token_t
*) cur
;
346 if (!is_white_space(t
)) {
347 cmd
= cmd_findword(*list
,&(t
->token
));
349 cmd
= KMALLOC(sizeof(cmdtab_t
)+strlen(&(t
->token
))+1,0);
350 memset(cmd
,0,sizeof(cmdtab_t
));
351 cmd
->cmdword
= (char *) (cmd
+1);
352 strcpy(cmd
->cmdword
,&(t
->token
));
353 cmd
->sibling
= *list
;
356 list
= &(cmd
->child
);
361 cmd_free_tokens(&tokens
);
369 cmd
->switches
= switches
;
376 static void _dumpindented(char *str
,int amt
)
383 dupstr
= strdup(str
);
388 for (idx
= 0; idx
< amt
; idx
++) printf(" ");
390 end
= strchr(ptr
,'\n');
392 if (end
) *end
++ = '\0';
393 else end
= ptr
+ strlen(ptr
);
402 static void _dumpswitches(char *str
)
410 switches
= strdup(str
);
415 end
= strchr(ptr
,'|');
416 if (end
) *end
++ = '\0';
417 else end
= ptr
+ strlen(ptr
);
420 if ((semi
= strchr(ptr
,';'))) {
422 newline
= strchr(semi
,'\n');
423 if (newline
) *newline
++ = '\0';
424 printf("%-12s %s\n",ptr
,semi
);
425 if (newline
) _dumpindented(newline
,5+12+1);
428 printf("%-12s (no information)\n",ptr
);
436 static void _dumpcmds(cmdtab_t
*cmd
,int level
,char **words
,int verbose
)
443 words
[level
] = cmd
->cmdword
;
445 for (idx
= 0; idx
< level
; idx
++) {
446 printf("%s ",words
[idx
]);
447 len
+= strlen(words
[idx
])+1;
449 printf("%s",cmd
->cmdword
);
450 len
+= strlen(cmd
->cmdword
);
451 for (idx
= len
; idx
< 20; idx
++) printf(" ");
452 printf("%s\n",cmd
->help
);
455 _dumpindented(cmd
->usage
,5);
457 _dumpswitches(cmd
->switches
);
461 _dumpcmds(cmd
->child
,level
+1,words
,verbose
);
466 static void dumpcmds(int verbose
)
470 _dumpcmds(cmd_root
,0,words
,verbose
);
474 static void _showpossible(ui_cmdline_t
*cline
,cmdtab_t
*cmd
)
478 if (cline
->argidx
== 0) {
479 printf("Available commands: ");
482 printf("Available \"");
483 for (i
= 0; i
< cline
->argidx
; i
++) {
484 printf("%s%s",(i
== 0) ? "" : " ",cline
->argv
[i
]);
486 printf("\" commands: ");
490 printf("%s",cmd
->cmdword
);
491 if (cmd
->sibling
) printf(", ");
498 static int cmd_help(ui_cmdline_t
*cmd
,int argc
,char *argv
[])
505 printf("Available commands:\n\n");
508 printf("For more information about a command, enter 'help command-name'\n");
516 cword
= cmd_findword(*tab
,argv
[idx
]);
518 if (cword
->func
!= NULL
) break;
520 tab
= &(cword
->child
);
521 if (idx
>= argc
) break;
525 printf("No help available for '%s'.\n\n",argv
[idx
]);
526 printf("Type 'help' for a list of commands.\n");
530 if (!cword
->func
&& (idx
>= argc
)) {
531 printf("No help available for '%s'.\n\n",cword
->cmdword
);
532 printf("Type 'help' for a list of commands.\n");
536 printf("\n SUMMARY\n\n");
537 _dumpindented(cword
->help
,5);
538 printf("\n USAGE\n\n");
539 _dumpindented(cword
->usage
,5);
540 if (cword
->switches
&& cword
->switches
[0]) {
541 printf("\n OPTIONS\n\n");
542 _dumpswitches(cword
->switches
);
557 "Obtain help for CFE commands",
559 "Without any parameters, the 'help' command will display a summary\n"
560 "of available commands. For more details on a command, type 'help'\n"
561 "and the command name.",
566 int cmd_lookup(queue_t
*head
,ui_cmdline_t
*cmd
)
573 * Reset the command line
576 memset(cmd
,0,sizeof(ui_cmdline_t
));
579 * Break it up into tokens
582 cmd_build_cmdline(head
, cmd
);
584 if (cmd
->argc
== 0) return CMD_ERR_BLANK
;
587 * Start walking the tree looking for a function
596 cword
= cmd_findword(*tab
,cmd
->argv
[idx
]);
598 if (cword
->func
!= NULL
) break;
600 tab
= &(cword
->child
);
601 if (idx
>= cmd
->argc
) break;
608 printf("Invalid command: \"%s\"\n", cmd
->argv
[idx
]);
609 _showpossible(cmd
,*tab
);
611 return CMD_ERR_INVALID
;
614 if (!cword
->func
&& (idx
>= cmd
->argc
)) {
615 printf("Incomplete command: \"%s\"\n",cmd
->argv
[idx
-1]);
616 _showpossible(cmd
,*tab
);
618 return CMD_ERR_AMBIGUOUS
;
622 cmd
->ref
= cword
->ref
;
623 cmd
->usage
= cword
->usage
;
624 cmd
->switches
= cword
->switches
;
625 cmd
->func
= cword
->func
;
631 void cmd_showusage(ui_cmdline_t
*cmd
)
634 _dumpindented(cmd
->usage
,5);
636 if (cmd
->switches
[0]) {
637 _dumpswitches(cmd
->switches
);
643 static void cmd_eat_leading_white(queue_t
*head
)
647 while (!q_isempty(head
)) {
648 t
= (ui_token_t
*) q_getfirst(head
);
649 if (is_white_space(t
)) {
657 ui_command_t
*cmd_readcommand(queue_t
*head
)
660 int insquote
= FALSE
;
661 int indquote
= FALSE
;
663 int term
= CMD_TERM_EOL
;
666 cmd_eat_leading_white(head
);
668 if (q_isempty(head
)) return NULL
;
670 cmd
= (ui_command_t
*) KMALLOC(sizeof(ui_command_t
),0);
671 q_init(&(cmd
->head
));
673 while ((t
= (ui_token_t
*) q_deqnext(head
))) {
677 if (!insquote
&& !indquote
) {
678 if ((*ptr
== ';') || (*ptr
== '\n')) {
679 term
= CMD_TERM_SEMI
;
682 if ((*ptr
== '&') && (*(ptr
+1) == '&')) {
686 if ((*ptr
== '|') && (*(ptr
+1) == '|')) {
693 insquote
= !insquote
;
698 indquote
= !indquote
;
702 q_enqueue(&(cmd
->head
),&(t
->qb
));
708 /* If we got out by finding a command separator, eat the separator */
709 if (term
!= CMD_TERM_EOL
) {
718 static ui_token_t
*make_token(char *str
,int len
)
720 ui_token_t
*t
= (ui_token_t
*) KMALLOC(sizeof(ui_token_t
) + len
,0);
722 memcpy(&(t
->token
),str
,len
);
723 (&(t
->token
))[len
] = 0;
728 void cmd_build_list(queue_t
*qb
,char *buf
)
730 char *cur
= buf
, *start
= NULL
, *fin
= NULL
;
737 if (*cur
== '&' && *(cur
+ 1) != '&') {
738 /* Do nothing if we have only one & */
740 else if (*cur
== '|' && *(cur
+ 1) != '|') {
741 /* Do nothing if we have only one | */
743 else if (((*cur
== ' ')||(*cur
== '\t')) &&
744 ((*(cur
- 1) == ' ')||(*(cur
- 1) == '\t'))) {
745 /* Make one big token for white space */
749 if (strchr(tokenbreaks
,*cur
)) {
752 t
= make_token(start
,fin
-start
);
753 q_enqueue(qb
,&(t
->qb
));
754 start
= cur
; /* Start new token */
758 /* If we are on a normal character but the last character was */
759 /* a special char we need to start a new token */
761 if ((cur
> buf
) && strchr(tokenbreaks
,*(cur
-1))) {
763 t
= make_token(start
,fin
-start
);
764 q_enqueue(qb
,&(t
->qb
));
765 start
= cur
; /* Start new token */
768 /* If the last charecter wasn't special keep going with */
782 t
= make_token(start
,fin
-start
);
783 q_enqueue(qb
,&(t
->qb
));
789 static int is_command_separator(ui_token_t
*t
)
791 char *string
= &(t
->token
);
799 if(*(string
+ 1) == '&')
803 if(*(string
+ 1) == '|')
812 static char *cmd_eat_quoted_arg(queue_t
*head
,ui_token_t
*t
)
822 * If it's not a quoted string, just return this token.
825 if (!myisquote(t
->token
)) {
826 dest
= lib_strdup(&(t
->token
));
827 /* Note: caller deletes original token */
832 * Otherwise, eat tokens in the quotes.
837 if (t
->token
== '"') dquote
= 1;
838 else squote
= 1; /* must be one or the other */
840 t
= (ui_token_t
*) q_deqnext(head
);
842 /* A single quote can only be terminated by another single quote */
843 if (squote
&& (t
->token
== '\'')) {
847 /* A double quote is only honored if not in a single quote */
848 if (dquote
&& !squote
&& (t
->token
== '\"')) {
852 /* Otherwise, keep this token. */
853 q_enqueue(&qlist
,(queue_t
*) t
);
854 t
= (ui_token_t
*) q_deqnext(head
);
858 * Go back through what we collected and figure out the string length.
861 for (q
= qlist
.q_next
; q
!= &qlist
; q
= q
->q_next
) {
862 maxlen
+= strlen(&(((ui_token_t
*) q
)->token
));
865 dest
= KMALLOC(maxlen
+1,0);
866 if (!dest
) return NULL
;
870 while ((t
= (ui_token_t
*) q_deqnext(&qlist
))) {
871 strcat(dest
,&(t
->token
));
878 static void cmd_append_tokens(queue_t
*qb
,char *str
)
883 cmd_build_list(&explist
,str
);
885 while ((qq
= q_deqnext(&explist
))) {
892 void cmd_walk_and_expand (queue_t
*qb
)
897 int alias_check
= TRUE
;
898 int insquote
= FALSE
;
903 while ((t
= (ui_token_t
*) q_deqnext(qb
))) {
904 if (t
->token
== '\'') {
906 insquote
= !insquote
;
907 /* Check to see if we should try to expand this token */
909 else if (!insquote
) {
910 if (alias_check
&& !strchr(tokenbreaks
,t
->token
) &&
911 (envstr
= env_getenv(&(t
->token
)))) {
912 /* Aliases: stick into token stream if no environment found */
913 cmd_append_tokens(&newq
,envstr
);
917 else if (t
->token
== '$') {
918 /* non-aliases: remove from token stream if no env found */
919 envstr
= env_getenv(&(t
->token
)+1);
920 if (envstr
) cmd_append_tokens(&newq
,envstr
);
925 /* Drop down below, keep this token as-is and append */
930 * If token was not removed, add it to the new queue
934 q_enqueue(&newq
,&(t
->qb
));
935 alias_check
= is_command_separator(t
);
941 * Put everything back on the original list.
944 while ((q
= q_deqnext(&newq
))) {
950 void cmd_free_tokens(queue_t
*list
)
954 while ((q
= q_deqnext(list
))) {