1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  UI Command Dispatch			File: ui_command.c
5    *
6    *  This module contains routines to maintain the command table,
7    *  parse and execute commands
8    *
9    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
10    *
11    *********************************************************************
12    *
13    *  Copyright 2000,2001,2002,2003
14    *  Broadcom Corporation. All rights reserved.
15    *
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.
22    *
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.
26    *
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.
32    *
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    ********************************************************************* */
47
48
49#include <stdarg.h>
50
51#include "lib_queue.h"
52
53#include "lib_types.h"
54#include "lib_string.h"
55#include "lib_malloc.h"
56#include "lib_printf.h"
57
58#include "cfe_iocb.h"
59#include "cfe_device.h"
60#include "cfe_console.h"
61#include "cfe_error.h"
62#include "env_subr.h"
63#include "cfe.h"
64
65#include "ui_command.h"
66
67#define MAX_EXPAND	16
68
69typedef struct cmdtab_s {
70    struct cmdtab_s *sibling;
71    struct cmdtab_s *child;
72    char *cmdword;
73    int (*func)(ui_cmdline_t *,int argc,char *argv[]);
74    void *ref;
75    char *help;
76    char *usage;
77    char *switches;
78} cmdtab_t;
79
80cmdtab_t *cmd_root;
81
82
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)=='"'))
86
87char *varchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
88                        "abcdefghijklmnopqrstuvwxyz"
89                        "0123456789_?";
90
91static char *tokenbreaks = " =\t\n\'\"&|;";
92static char *spacechars = " \t";
93
94static char *cmd_eat_quoted_arg(queue_t *head,ui_token_t *t);
95
96
97static inline int is_white_space(ui_token_t *t)
98{
99    return (strchr(spacechars,t->token) != NULL);
100}
101
102int cmd_sw_value(ui_cmdline_t *cmd,char *swname,char **swvalue)
103{
104    int idx;
105
106    for (idx = 0; idx < cmd->swc; idx++) {
107	if (strcmp(swname,cmd->swv[idx].swname) == 0) {
108	    *swvalue = cmd->swv[idx].swvalue;
109	    return 1;
110	    }
111	}
112
113    return 0;
114}
115
116int cmd_sw_posn(ui_cmdline_t *cmd,char *swname)
117{
118    int idx;
119
120    for (idx = 0; idx < cmd->swc; idx++) {
121	if (strcmp(swname,cmd->swv[idx].swname) == 0) {
122	    return cmd->swv[idx].swidx;
123	    }
124	}
125
126    return -1;
127}
128
129char *cmd_sw_name(ui_cmdline_t *cmd,int swidx)
130{
131    if ((swidx < 0) || (swidx >= cmd->swc)) return NULL;
132
133    return cmd->swv[swidx].swname;
134}
135
136
137int cmd_sw_isset(ui_cmdline_t *cmd,char *swname)
138{
139    int idx;
140
141    for (idx = 0; idx < cmd->swc; idx++) {
142	if (strcmp(swname,cmd->swv[idx].swname) == 0) {
143	    return 1;
144	    }
145	}
146
147    return 0;
148}
149
150char *cmd_getarg(ui_cmdline_t *cmd,int argnum)
151{
152    argnum += cmd->argidx;
153    if ((argnum < 0) || (argnum >= cmd->argc)) return NULL;
154    return cmd->argv[argnum];
155}
156
157void cmd_free(ui_cmdline_t *cmd)
158{
159    int idx;
160
161    for (idx = 0; idx < cmd->argc; idx++) {
162	KFREE(cmd->argv[idx]);
163	}
164
165    for (idx = 0; idx < cmd->swc; idx++) {
166	KFREE(cmd->swv[idx].swname);
167	}
168
169    cmd->argc = 0;
170    cmd->swc = 0;
171}
172
173int cmd_sw_validate(ui_cmdline_t *cmd,char *validstr)
174{
175    char *vdup;
176    char *vptr;
177    char *vnext;
178    char atype;
179    char *x;
180    int idx;
181    int valid;
182
183    if (cmd->swc == 0) return -1;
184
185    vdup = strdup(validstr);
186
187    for (idx = 0; idx < cmd->swc; idx++) {
188	vptr = vdup;
189
190	vnext = vptr;
191	valid = 0;
192
193	while (vnext) {
194
195	    /*
196	     * Eat the next switch description from the valid string
197	     */
198	    x = strchr(vptr,'|');
199	    if (x) {
200		*x = '\0';
201		vnext = x+1;
202		}
203	    else {
204		vnext = NULL;
205		}
206
207	    /*
208	     * Get the expected arg type, if any
209	     */
210	    x = strchr(vptr,'=');
211	    if (x) {
212		atype = *(x+1);
213		*x = 0;
214		}
215	    else {
216		if ((x = strchr(vptr,';'))) *x = 0;
217		atype = 0;
218		}
219
220
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)) {
224		    valid = 1;
225		    }
226		/* value needed and supplied */
227		if ((atype != 0) && (cmd->swv[idx].swvalue != NULL)) {
228		    valid = 1;
229		    }
230		strcpy(vdup,validstr);
231		break;
232		}
233
234	    /*
235	     * Otherwise, next!
236	     */
237
238	    strcpy(vdup,validstr);
239	    vptr = vnext;
240	    }
241
242	/*
243	 * If not valid, return index of bad switch
244	 */
245
246	if (valid == 0) {
247	    KFREE(vdup);
248	    return idx;
249	    }
250
251	}
252
253    /*
254     * Return -1 if everything went well.  A little strange,
255     * but it's easier this way.
256     */
257
258    KFREE(vdup);
259    return -1;
260}
261
262static cmdtab_t *cmd_findword(cmdtab_t *list,char *cmdword)
263{
264    while (list) {
265	if (strcmp(cmdword,list->cmdword) == 0) return list;
266	list = list->sibling;
267	}
268
269    return NULL;
270}
271
272
273void cmd_build_cmdline(queue_t *head, ui_cmdline_t *cmd)
274{
275    ui_token_t *t;
276    ui_token_t *next;
277
278    memset(cmd, 0, sizeof(ui_cmdline_t));
279
280    t = (ui_token_t *) q_deqnext(head);
281
282    while (t != NULL) {
283	if (is_white_space(t)) {
284	    /* do nothing */
285	    }
286	else if (t->token != '-') {
287	    if(cmd->argc < MAX_TOKENS){
288		cmd->argv[cmd->argc] = cmd_eat_quoted_arg(head,t);
289		cmd->argc++;
290		}
291	    /* Token is a switch */
292	    }
293	else {
294	    if (cmd->swc < MAX_SWITCHES) {
295		cmd->swv[cmd->swc].swname = lib_strdup(&(t->token));
296
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 */
304			if (t != NULL) {
305			    cmd->swv[cmd->swc].swvalue = cmd_eat_quoted_arg(head,t);
306			    }
307			}
308		    else {					/* no value */
309			cmd->swv[cmd->swc].swvalue = NULL;
310			}
311		    }
312		/*
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.
316		 */
317		cmd->swv[cmd->swc].swidx = cmd->argc;
318		cmd->swc++;
319		}
320	    }
321	KFREE(t);
322	t = (ui_token_t *) q_deqnext(head);
323	}
324
325
326}
327
328int cmd_addcmd(char *command,
329	       int (*func)(ui_cmdline_t *,int argc,char *argv[]),
330	       void *ref,
331	       char *help,
332	       char *usage,
333	       char *switches)
334{
335    cmdtab_t **list = &cmd_root;
336    cmdtab_t *cmd = NULL;
337    queue_t tokens;
338    queue_t *cur;
339    ui_token_t *t;
340
341    cmd_build_list(&tokens,command);
342    cur = tokens.q_next;
343
344    while (cur != &tokens) {
345	t = (ui_token_t *) cur;
346	if (!is_white_space(t)) {
347	    cmd = cmd_findword(*list,&(t->token));
348	    if (!cmd) {
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;
354		*list = cmd;
355		}
356	    list = &(cmd->child);
357	    }
358	cur = cur->q_next;
359	}
360
361    cmd_free_tokens(&tokens);
362
363    if (!cmd) return -1;
364
365    cmd->func = func;
366    cmd->usage = usage;
367    cmd->ref = ref;
368    cmd->help = help;
369    cmd->switches = switches;
370
371    return 0;
372}
373
374
375
376static void _dumpindented(char *str,int amt)
377{
378    int idx;
379    char *dupstr;
380    char *end;
381    char *ptr;
382
383    dupstr = strdup(str);
384
385    ptr = dupstr;
386
387    while (*ptr) {
388	for (idx = 0; idx < amt; idx++) printf(" ");
389
390	end = strchr(ptr,'\n');
391
392	if (end) *end++ = '\0';
393	else end = ptr + strlen(ptr);
394
395	printf("%s\n",ptr);
396	ptr = end;
397	}
398
399    KFREE(dupstr);
400}
401
402static void _dumpswitches(char *str)
403{
404    char *switches;
405    char *end;
406    char *ptr;
407    char *semi;
408    char *newline;
409
410    switches = strdup(str);
411
412    ptr = switches;
413
414    while (*ptr) {
415	end = strchr(ptr,'|');
416	if (end) *end++ = '\0';
417	else end = ptr + strlen(ptr);
418
419	printf("     ");
420	if ((semi = strchr(ptr,';'))) {
421	    *semi++ = '\0';
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);
426	    }
427	else {
428	    printf("%-12s (no information)\n",ptr);
429	    }
430	ptr = end;
431	}
432
433    KFREE(switches);
434}
435
436static void _dumpcmds(cmdtab_t *cmd,int level,char **words,int verbose)
437{
438    int idx;
439    int len;
440
441    while (cmd) {
442	len = 0;
443	words[level] = cmd->cmdword;
444	if (cmd->func) {
445	    for (idx = 0; idx < level; idx++) {
446		printf("%s ",words[idx]);
447		len += strlen(words[idx])+1;
448		}
449	    printf("%s",cmd->cmdword);
450	    len += strlen(cmd->cmdword);
451	    for (idx = len; idx < 20; idx++) printf(" ");
452	    printf("%s\n",cmd->help);
453	    if (verbose) {
454		printf("\n");
455		_dumpindented(cmd->usage,5);
456		printf("\n");
457		_dumpswitches(cmd->switches);
458		printf("\n");
459		}
460	    }
461	_dumpcmds(cmd->child,level+1,words,verbose);
462	cmd = cmd->sibling;
463	}
464}
465
466static void dumpcmds(int verbose)
467{
468    char *words[20];
469
470    _dumpcmds(cmd_root,0,words,verbose);
471}
472
473
474static void _showpossible(ui_cmdline_t *cline,cmdtab_t *cmd)
475{
476    int i;
477
478    if (cline->argidx == 0) {
479	printf("Available commands: ");
480	}
481    else {
482	printf("Available \"");
483	for (i = 0; i < cline->argidx; i++) {
484	    printf("%s%s",(i == 0) ? "" : " ",cline->argv[i]);
485	    }
486	printf("\" commands: ");
487	}
488
489    while (cmd) {
490	printf("%s",cmd->cmdword);
491	if (cmd->sibling) printf(", ");
492	cmd = cmd->sibling;
493	}
494
495    printf("\n");
496}
497
498static int cmd_help(ui_cmdline_t *cmd,int argc,char *argv[])
499{
500    cmdtab_t **tab;
501    cmdtab_t *cword;
502    int idx;
503
504    if (argc == 0) {
505	printf("Available commands:\n\n");
506	dumpcmds(0);
507	printf("\n");
508	printf("For more information about a command, enter 'help command-name'\n");
509	}
510    else {
511	idx = 0;
512	tab = &cmd_root;
513	cword = NULL;
514
515	for (;;) {
516	    cword = cmd_findword(*tab,argv[idx]);
517	    if (!cword) break;
518	    if (cword->func != NULL) break;
519	    idx++;
520	    tab = &(cword->child);
521	    if (idx >= argc) break;
522	    }
523
524	if (cword == NULL) {
525	    printf("No help available for '%s'.\n\n",argv[idx]);
526	    printf("Type 'help' for a list of commands.\n");
527	    return -1;
528	    }
529
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");
533	    return -1;
534	    }
535
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);
543	    }
544	printf("\n");
545	}
546
547    return 0;
548}
549
550void cmd_init(void)
551{
552    cmd_root = NULL;
553
554    cmd_addcmd("help",
555	       cmd_help,
556	       NULL,
557	       "Obtain help for CFE commands",
558#ifndef CFG_MINIMAL_SIZE
559	       "help [command]\n\n"
560	       "Without any parameters, the 'help' command will display a summary\n"
561	       "of available commands.  For more details on a command, type 'help'\n"
562	       "and the command name.",
563#else /* CFG_MINIMAL_SIZE */
564	       "help [command]\n",
565#endif /* CFG_MINIMAL_SIZE */
566	       "");
567}
568
569
570int cmd_lookup(queue_t *head,ui_cmdline_t *cmd)
571{
572    cmdtab_t **tab;
573    cmdtab_t *cword;
574    int idx;
575
576    /*
577     * Reset the command line
578     */
579
580    memset(cmd,0,sizeof(ui_cmdline_t));
581
582    /*
583     * Break it up into tokens
584     */
585
586    cmd_build_cmdline(head, cmd);
587
588    if (cmd->argc == 0) return CMD_ERR_BLANK;
589
590    /*
591     * Start walking the tree looking for a function
592     * to execute.
593     */
594
595    idx = 0;
596    tab = &cmd_root;
597    cword = NULL;
598
599    for (;;) {
600	cword = cmd_findword(*tab,cmd->argv[idx]);
601	if (!cword) break;
602	if (cword->func != NULL) break;
603	idx++;
604	tab = &(cword->child);
605	if (idx >= cmd->argc) break;
606	}
607
608    cmd->argidx = idx;
609
610
611    if (cword == NULL) {
612	printf("Invalid command: \"%s\"\n", cmd->argv[idx]);
613	_showpossible(cmd,*tab);
614	printf("\n");
615	return CMD_ERR_INVALID;
616	}
617
618    if (!cword->func && (idx >= cmd->argc)) {
619	printf("Incomplete command: \"%s\"\n",cmd->argv[idx-1]);
620	_showpossible(cmd,*tab);
621	printf("\n");
622	return CMD_ERR_AMBIGUOUS;
623	}
624
625    cmd->argidx++;
626    cmd->ref = cword->ref;
627    cmd->usage = cword->usage;
628    cmd->switches = cword->switches;
629    cmd->func = cword->func;
630
631    return 0;
632}
633
634
635void cmd_showusage(ui_cmdline_t *cmd)
636{
637    printf("\n");
638    _dumpindented(cmd->usage,5);
639    printf("\n");
640    if (cmd->switches[0]) {
641	_dumpswitches(cmd->switches);
642	printf("\n");
643	}
644}
645
646
647static void cmd_eat_leading_white(queue_t *head)
648{
649    ui_token_t *t;
650
651    while (!q_isempty(head)) {
652	t = (ui_token_t *) q_getfirst(head);
653	if (is_white_space(t)) {
654	    q_dequeue(&(t->qb));
655	    KFREE(t);
656	    }
657	else break;
658	}
659}
660
661ui_command_t *cmd_readcommand(queue_t *head)
662{
663    char *ptr;
664    int insquote = FALSE;
665    int indquote = FALSE;
666    ui_command_t *cmd;
667    int term = CMD_TERM_EOL;
668    ui_token_t *t;
669
670    cmd_eat_leading_white(head);
671
672    if (q_isempty(head)) return NULL;
673
674    cmd = (ui_command_t *) KMALLOC(sizeof(ui_command_t),0);
675    q_init(&(cmd->head));
676
677    while ((t = (ui_token_t *) q_deqnext(head))) {
678
679	ptr = &(t->token);
680
681	if (!insquote && !indquote) {
682	    if ((*ptr == ';') || (*ptr == '\n')) {
683		term = CMD_TERM_SEMI;
684		break;
685		}
686	    if ((*ptr == '&') && (*(ptr+1) == '&')) {
687		term = CMD_TERM_AND;
688		break;
689		}
690	    if ((*ptr == '|') && (*(ptr+1) == '|')) {
691		term = CMD_TERM_OR;
692		break;
693		}
694	    }
695
696	if (*ptr == '\'') {
697	    insquote = !insquote;
698	    }
699
700	if (!insquote) {
701	    if (*ptr == '"') {
702		indquote = !indquote;
703		}
704	    }
705
706	q_enqueue(&(cmd->head),&(t->qb));
707
708	}
709
710    cmd->term = term;
711
712    /* If we got out by finding a command separator, eat the separator */
713    if (term != CMD_TERM_EOL) {
714	KFREE(t);
715	}
716
717    return cmd;
718}
719
720
721
722static ui_token_t *make_token(char *str,int len)
723{
724    ui_token_t *t = (ui_token_t *) KMALLOC(sizeof(ui_token_t) + len,0);
725
726    memcpy(&(t->token),str,len);
727    (&(t->token))[len] = 0;
728
729    return t;
730}
731
732void cmd_build_list(queue_t *qb,char *buf)
733{
734    char *cur = buf, *start = NULL, *fin = NULL;
735    ui_token_t *t;
736
737    q_init(qb);
738
739    start = cur;
740    while(*cur != '\0'){
741	if (*cur == '&' && *(cur + 1) != '&') {
742	    /* Do nothing if we have only one & */
743	    }
744	else if (*cur == '|' && *(cur + 1) != '|') {
745	    /* Do nothing if we have only one | */
746	    }
747	else if (((*cur == ' ')||(*cur == '\t')) &&
748		 ((*(cur - 1) == ' ')||(*(cur - 1) == '\t'))) {
749	    /* Make one big token for white space */
750	    }
751	else {
752
753	    if (strchr(tokenbreaks,*cur)) {
754		if (cur != buf) {
755		    fin = cur;
756		    t = make_token(start,fin-start);
757		    q_enqueue(qb,&(t->qb));
758		    start = cur; /* Start new token */
759		    }
760		}
761	    else {
762		/* If we are on a normal character but the last character was */
763		/* a special char we need to start a new token */
764
765		if ((cur > buf) && strchr(tokenbreaks,*(cur-1))) {
766		    fin = cur;
767		    t = make_token(start,fin-start);
768		    q_enqueue(qb,&(t->qb));
769		    start = cur; /* Start new token */
770		    }
771		else {
772		    /* If the last charecter wasn't special keep going with */
773		    /* current token */
774		    }
775
776
777		}
778
779	    }
780	cur++;
781	}
782
783    fin = cur;
784
785    if (fin-start > 0) {
786	t = make_token(start,fin-start);
787	q_enqueue(qb,&(t->qb));
788	}
789
790    return;
791}
792
793static int is_command_separator(ui_token_t *t)
794{
795    char *string = &(t->token);
796    int sep = 0;
797
798    switch(*string){
799	case ';':
800	    sep = 1;
801	    break;
802	case '&':
803	    if(*(string + 1) == '&')
804		sep = 1;
805	    break;
806	case '|':
807	    if(*(string + 1) == '|')
808		sep = 1;
809	default:
810	    break;
811	}
812
813    return(sep);
814}
815
816static char *cmd_eat_quoted_arg(queue_t *head,ui_token_t *t)
817{
818    int dquote = 0;
819    int squote = 0;
820    queue_t qlist;
821    queue_t *q;
822    char *dest;
823    int maxlen = 0;
824
825    /*
826     * If it's not a quoted string, just return this token.
827     */
828
829    if (!myisquote(t->token)) {
830	dest = lib_strdup(&(t->token));
831	/* Note: caller deletes original token */
832	return dest;
833	}
834
835    /*
836     * Otherwise, eat tokens in the quotes.
837     */
838
839    q_init(&qlist);
840
841    if (t->token == '"') dquote = 1;
842    else squote = 1;			/* must be one or the other */
843
844    t = (ui_token_t *) q_deqnext(head);
845    while (t != NULL) {
846	/* A single quote can only be terminated by another single quote */
847	if (squote && (t->token == '\'')) {
848	    KFREE(t);
849	    break;
850	    }
851	/* A double quote is only honored if not in a single quote */
852	if (dquote && !squote && (t->token == '\"')) {
853	    KFREE(t);
854	    break;
855	    }
856	/* Otherwise, keep this token. */
857	q_enqueue(&qlist,(queue_t *) t);
858	t = (ui_token_t *) q_deqnext(head);
859	}
860
861    /*
862     * Go back through what we collected and figure out the string length.
863     */
864
865    for (q = qlist.q_next; q != &qlist; q = q->q_next) {
866	maxlen += strlen(&(((ui_token_t *) q)->token));
867	}
868
869    dest = KMALLOC(maxlen+1,0);
870    if (!dest) return NULL;
871
872    *dest = '\0';
873
874    while ((t = (ui_token_t *) q_deqnext(&qlist))) {
875	strcat(dest,&(t->token));
876	KFREE(t);
877	}
878
879    return dest;
880}
881
882static void cmd_append_tokens(queue_t *qb,char *str)
883{
884    queue_t *qq;
885    queue_t explist;
886
887    cmd_build_list(&explist,str);
888
889    while ((qq = q_deqnext(&explist))) {
890	q_enqueue(qb,qq);
891	}
892}
893
894
895
896void cmd_walk_and_expand (queue_t *qb)
897{
898    queue_t *q;
899    queue_t newq;
900    ui_token_t *t;
901    int alias_check = TRUE;
902    int insquote = FALSE;
903    char *envstr;
904
905    q_init(&newq);
906
907    while ((t = (ui_token_t *) q_deqnext(qb))) {
908	if (t->token == '\'') {
909	    alias_check = FALSE;
910	    insquote = !insquote;
911	    /* Check to see if we should try to expand this token */
912	    }
913	else if (!insquote) {
914	    if (alias_check && !strchr(tokenbreaks,t->token) &&
915		(envstr = env_getenv(&(t->token)))) {
916		/* Aliases: stick into token stream if no environment found */
917		cmd_append_tokens(&newq,envstr);
918		KFREE(t);
919		t = NULL;
920		}
921	    else if (t->token == '$') {
922		/* non-aliases: remove from token stream if no env found */
923		envstr = env_getenv(&(t->token)+1);
924		if (envstr) cmd_append_tokens(&newq,envstr);
925		KFREE(t);
926		t = NULL;
927		}
928	    else {
929		/* Drop down below, keep this token as-is and append */
930		}
931	    }
932
933	/*
934	 * If token was not removed, add it to the new queue
935	 */
936
937	if (t) {
938	    q_enqueue(&newq,&(t->qb));
939	    alias_check = is_command_separator(t);
940	    }
941
942	}
943
944    /*
945     * Put everything back on the original list.
946     */
947
948    while ((q = q_deqnext(&newq))) {
949	q_enqueue(qb,q);
950	}
951
952}
953
954void cmd_free_tokens(queue_t *list)
955{
956    queue_t *q;
957
958    while ((q = q_deqnext(list))) {
959	KFREE(q);
960	}
961}
962
963