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