Skip to content
Snippets Groups Projects
cli_hush.c 93.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    			if (ch==EOF) {
    				syntax();
    				return 1;
    			}
    			break;
    		case '"':
    			dest->nonnull = 1;
    			dest->quote = !dest->quote;
    			break;
    #ifndef __U_BOOT__
    		case '`':
    			process_command_subs(dest, ctx, input, '`');
    			break;
    		case '>':
    			redir_fd = redirect_opt_num(dest);
    			done_word(dest, ctx);
    			redir_style=REDIRECT_OVERWRITE;
    			if (next == '>') {
    				redir_style=REDIRECT_APPEND;
    				b_getch(input);
    			} else if (next == '(') {
    				syntax();   /* until we support >(list) Process Substitution */
    				return 1;
    			}
    			setup_redirect(ctx, redir_fd, redir_style, input);
    			break;
    		case '<':
    			redir_fd = redirect_opt_num(dest);
    			done_word(dest, ctx);
    			redir_style=REDIRECT_INPUT;
    			if (next == '<') {
    				redir_style=REDIRECT_HEREIS;
    				b_getch(input);
    			} else if (next == '>') {
    				redir_style=REDIRECT_IO;
    				b_getch(input);
    			} else if (next == '(') {
    				syntax();   /* until we support <(list) Process Substitution */
    				return 1;
    			}
    			setup_redirect(ctx, redir_fd, redir_style, input);
    			break;
    #endif
    		case ';':
    			done_word(dest, ctx);
    			done_pipe(ctx,PIPE_SEQ);
    			break;
    		case '&':
    			done_word(dest, ctx);
    			if (next=='&') {
    				b_getch(input);
    				done_pipe(ctx,PIPE_AND);
    			} else {
    #ifndef __U_BOOT__
    				done_pipe(ctx,PIPE_BG);
    #else
    				syntax_err();
    				return 1;
    #endif
    			}
    			break;
    		case '|':
    			done_word(dest, ctx);
    			if (next=='|') {
    				b_getch(input);
    				done_pipe(ctx,PIPE_OR);
    			} else {
    				/* we could pick up a file descriptor choice here
    				 * with redirect_opt_num(), but bash doesn't do it.
    				 * "echo foo 2| cat" yields "foo 2". */
    #ifndef __U_BOOT__
    				done_command(ctx);
    #else
    				syntax_err();
    				return 1;
    #endif
    			}
    			break;
    #ifndef __U_BOOT__
    		case '(':
    		case '{':
    			if (parse_group(dest, ctx, input, ch)!=0) return 1;
    			break;
    		case ')':
    		case '}':
    			syntax();   /* Proper use of this character caught by end_trigger */
    			return 1;
    			break;
    #endif
    
    		case SUBSTED_VAR_SYMBOL:
    			dest->nonnull = 1;
    			while (ch = b_getch(input), ch != EOF &&
    			    ch != SUBSTED_VAR_SYMBOL) {
    				debug_printf("subst, pass=%d\n", ch);
    				if (input->__promptme == 0)
    					return 1;
    				b_addchr(dest, ch);
    			}
    			debug_printf("subst, term=%d\n", ch);
    			if (ch == EOF) {
    				syntax();
    				return 1;
    			}
    			break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		default:
    			syntax();   /* this is really an internal logic error */
    			return 1;
    			}
    		}
    	}
    	/* complain if quote?  No, maybe we just finished a command substitution
    	 * that was quoted.  Example:
    	 * $ echo "`cat foo` plus more"
    	 * and we just got the EOF generated by the subshell that ran "cat foo"
    	 * The only real complaint is if we got an EOF when end_trigger != '\0',
    	 * that is, we were really supposed to get end_trigger, and never got
    	 * one before the EOF.  Can't use the standard "syntax error" return code,
    	 * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
    	debug_printf("leaving parse_stream (EOF)\n");
    	if (end_trigger != '\0') return -1;
    	return 0;
    }
    
    
    Kim Phillips's avatar
    Kim Phillips committed
    static void mapset(const unsigned char *set, int code)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	const unsigned char *s;
    	for (s=set; *s; s++) map[*s] = code;
    }
    
    
    Kim Phillips's avatar
    Kim Phillips committed
    static void update_ifs_map(void)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	/* char *ifs and char map[256] are both globals. */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	ifs = (uchar *)getenv("IFS");
    	if (ifs == NULL) ifs=(uchar *)" \t\n";
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* Precompute a list of 'flow through' behavior so it can be treated
    	 * quickly up front.  Computation is necessary because of IFS.
    	 * Special case handling of IFS == " \t\n" is not implemented.
    	 * The map[] array only really needs two bits each, and on most machines
    	 * that would be faster because of the reduced L1 cache footprint.
    	 */
    	memset(map,0,sizeof(map)); /* most characters flow through always */
    #ifndef __U_BOOT__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	mapset((uchar *)"\\$'\"`", 3);      /* never flow through */
    	mapset((uchar *)"<>;&|(){}#", 1);   /* flow through if quoted */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    
    	{
    		uchar subst[2] = {SUBSTED_VAR_SYMBOL, 0};
    		mapset(subst, 3);       /* never flow through */
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	mapset((uchar *)"\\$'\"", 3);       /* never flow through */
    	mapset((uchar *)";&|#", 1);         /* flow through if quoted */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    	mapset(ifs, 2);            /* also flow through if quoted */
    }
    
    /* most recursion does not come through here, the exeception is
     * from builtin_source() */
    
    Kim Phillips's avatar
    Kim Phillips committed
    static int parse_stream_outer(struct in_str *inp, int flag)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	struct p_context ctx;
    	o_string temp=NULL_O_STRING;
    	int rcode;
    #ifdef __U_BOOT__
    	int code = 0;
    #endif
    	do {
    		ctx.type = flag;
    		initialize_context(&ctx);
    		update_ifs_map();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		inp->promptmode=1;
    		rcode = parse_stream(&temp, &ctx, inp, '\n');
    #ifdef __U_BOOT__
    		if (rcode == 1) flag_repeat = 0;
    #endif
    		if (rcode != 1 && ctx.old_flag != 0) {
    			syntax();
    #ifdef __U_BOOT__
    			flag_repeat = 0;
    #endif
    		}
    		if (rcode != 1 && ctx.old_flag == 0) {
    			done_word(&temp, &ctx);
    			done_pipe(&ctx,PIPE_SEQ);
    #ifndef __U_BOOT__
    			run_list(ctx.list_head);
    #else
    
    			code = run_list(ctx.list_head);
    			if (code == -2) {	/* exit */
    				b_free(&temp);
    				code = 0;
    				/* XXX hackish way to not allow exit from main loop */
    				if (inp->peek == file_peek) {
    					printf("exit not allowed from main input shell.\n");
    					continue;
    				}
    				break;
    			}
    			if (code == -1)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			    flag_repeat = 0;
    #endif
    		} else {
    			if (ctx.old_flag != 0) {
    				free(ctx.stack);
    				b_reset(&temp);
    			}
    #ifdef __U_BOOT__
    			if (inp->__promptme == 0) printf("<INTERRUPT>\n");
    			inp->__promptme = 1;
    #endif
    			temp.nonnull = 0;
    			temp.quote = 0;
    			inp->p = NULL;
    			free_pipe_list(ctx.list_head,0);
    		}
    		b_free(&temp);
    	} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP));   /* loop on syntax errors, return on EOF */
    #ifndef __U_BOOT__
    	return 0;
    #else
    	return (code != 0) ? 1 : 0;
    #endif /* __U_BOOT__ */
    }
    
    #ifndef __U_BOOT__
    static int parse_string_outer(const char *s, int flag)
    #else
    
    int parse_string_outer(const char *s, int flag)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif	/* __U_BOOT__ */
    {
    	struct in_str input;
    #ifdef __U_BOOT__
    	char *p = NULL;
    	int rcode;
    	if ( !s || !*s)
    		return 1;
    	if (!(p = strchr(s, '\n')) || *++p) {
    		p = xmalloc(strlen(s) + 2);
    		strcpy(p, s);
    		strcat(p, "\n");
    		setup_string_in_str(&input, p);
    		rcode = parse_stream_outer(&input, flag);
    		free(p);
    		return rcode;
    	} else {
    #endif
    	setup_string_in_str(&input, s);
    	return parse_stream_outer(&input, flag);
    #ifdef __U_BOOT__
    	}
    #endif
    }
    
    #ifndef __U_BOOT__
    static int parse_file_outer(FILE *f)
    #else
    int parse_file_outer(void)
    #endif
    {
    	int rcode;
    	struct in_str input;
    #ifndef __U_BOOT__
    	setup_file_in_str(&input, f);
    #else
    	setup_file_in_str(&input);
    #endif
    	rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
    	return rcode;
    }
    
    #ifdef __U_BOOT__
    
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
    
    static void u_boot_hush_reloc(void)
    {
    	unsigned long addr;
    	struct reserved_combo *r;
    
    	for (r=reserved_list; r<reserved_list+NRES; r++) {
    		addr = (ulong) (r->literal) + gd->reloc_off;
    		r->literal = (char *)addr;
    	}
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    int u_boot_hush_start(void)
    {
    
    	if (top_vars == NULL) {
    		top_vars = malloc(sizeof(struct variables));
    		top_vars->name = "HUSH_VERSION";
    		top_vars->value = "0.01";
    
    Kim Phillips's avatar
    Kim Phillips committed
    		top_vars->next = NULL;
    
    		top_vars->flg_export = 0;
    		top_vars->flg_read_only = 1;
    
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return 0;
    }
    
    static void *xmalloc(size_t size)
    {
    	void *p = NULL;
    
    	if (!(p = malloc(size))) {
    	    printf("ERROR : memory not allocated\n");
    	    for(;;);
    	}
    	return p;
    }
    
    static void *xrealloc(void *ptr, size_t size)
    {
    	void *p = NULL;
    
    	if (!(p = realloc(ptr, size))) {
    	    printf("ERROR : memory not allocated\n");
    	    for(;;);
    	}
    	return p;
    }
    #endif /* __U_BOOT__ */
    
    #ifndef __U_BOOT__
    /* Make sure we have a controlling tty.  If we get started under a job
     * aware app (like bash for example), make sure we are now in charge so
     * we don't fight over who gets the foreground */
    
    static void setup_job_control(void)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	static pid_t shell_pgrp;
    	/* Loop until we are in the foreground.  */
    	while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ()))
    		kill (- shell_pgrp, SIGTTIN);
    
    	/* Ignore interactive and job-control signals.  */
    	signal(SIGINT, SIG_IGN);
    	signal(SIGQUIT, SIG_IGN);
    	signal(SIGTERM, SIG_IGN);
    	signal(SIGTSTP, SIG_IGN);
    	signal(SIGTTIN, SIG_IGN);
    	signal(SIGTTOU, SIG_IGN);
    	signal(SIGCHLD, SIG_IGN);
    
    	/* Put ourselves in our own process group.  */
    	setsid();
    	shell_pgrp = getpid ();
    	setpgid (shell_pgrp, shell_pgrp);
    
    	/* Grab control of the terminal.  */
    	tcsetpgrp(shell_terminal, shell_pgrp);
    }
    
    
    int hush_main(int argc, char * const *argv)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	int opt;
    	FILE *input;
    	char **e = environ;
    
    	/* XXX what should these be while sourcing /etc/profile? */
    	global_argc = argc;
    	global_argv = argv;
    
    	/* (re?) initialize globals.  Sometimes hush_main() ends up calling
    	 * hush_main(), therefore we cannot rely on the BSS to zero out this
    	 * stuff.  Reset these to 0 every time. */
    	ifs = NULL;
    	/* map[] is taken care of with call to update_ifs_map() */
    	fake_mode = 0;
    	interactive = 0;
    	close_me_head = NULL;
    	last_bg_pid = 0;
    	job_list = NULL;
    	last_jobid = 0;
    
    	/* Initialize some more globals to non-zero values */
    	set_cwd();
    
    #ifdef CONFIG_FEATURE_COMMAND_EDITING
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	cmdedit_set_initial_prompt();
    #else
    	PS1 = NULL;
    #endif
    	PS2 = "> ";
    
    	/* initialize our shell local variables with the values
    	 * currently living in the environment */
    	if (e) {
    		for (; *e; e++)
    			set_local_var(*e, 2);   /* without call putenv() */
    	}
    
    	last_return_code=EXIT_SUCCESS;
    
    
    	if (argv[0] && argv[0][0] == '-') {
    		debug_printf("\nsourcing /etc/profile\n");
    		if ((input = fopen("/etc/profile", "r")) != NULL) {
    			mark_open(fileno(input));
    			parse_file_outer(input);
    			mark_closed(fileno(input));
    			fclose(input);
    		}
    	}
    	input=stdin;
    
    	while ((opt = getopt(argc, argv, "c:xif")) > 0) {
    		switch (opt) {
    			case 'c':
    				{
    					global_argv = argv+optind;
    					global_argc = argc-optind;
    					opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
    					goto final_return;
    				}
    				break;
    			case 'i':
    				interactive++;
    				break;
    			case 'f':
    				fake_mode++;
    				break;
    			default:
    #ifndef BB_VER
    				fprintf(stderr, "Usage: sh [FILE]...\n"
    						"   or: sh -c command [args]...\n\n");
    				exit(EXIT_FAILURE);
    #else
    				show_usage();
    #endif
    		}
    	}
    	/* A shell is interactive if the `-i' flag was given, or if all of
    	 * the following conditions are met:
    	 *	  no -c command
    	 *    no arguments remaining or the -s flag given
    	 *    standard input is a terminal
    	 *    standard output is a terminal
    	 *    Refer to Posix.2, the description of the `sh' utility. */
    	if (argv[optind]==NULL && input==stdin &&
    			isatty(fileno(stdin)) && isatty(fileno(stdout))) {
    		interactive++;
    	}
    
    	debug_printf("\ninteractive=%d\n", interactive);
    	if (interactive) {
    		/* Looks like they want an interactive shell */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
    
    		printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n");
    		printf( "Enter 'help' for a list of built-in commands.\n\n");
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		setup_job_control();
    	}
    
    	if (argv[optind]==NULL) {
    		opt=parse_file_outer(stdin);
    		goto final_return;
    	}
    
    	debug_printf("\nrunning script '%s'\n", argv[optind]);
    	global_argv = argv+optind;
    	global_argc = argc-optind;
    	input = xfopen(argv[optind], "r");
    	opt = parse_file_outer(input);
    
    
    #ifdef CONFIG_FEATURE_CLEAN_UP
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	fclose(input);
    	if (cwd && cwd != unknown)
    		free((char*)cwd);
    	{
    		struct variables *cur, *tmp;
    		for(cur = top_vars; cur; cur = tmp) {
    			tmp = cur->next;
    			if (!cur->flg_read_only) {
    				free(cur->name);
    				free(cur->value);
    				free(cur);
    			}
    		}
    	}
    #endif
    
    final_return:
    	return(opt?opt:last_return_code);
    }
    #endif
    
    static char *insert_var_value(char *inp)
    
    {
    	return insert_var_value_sub(inp, 0);
    }
    
    static char *insert_var_value_sub(char *inp, int tag_subst)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	int res_str_len = 0;
    	int len;
    	int done = 0;
    	char *p, *p1, *res_str = NULL;
    
    	while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
    
    		/* check the beginning of the string for normal charachters */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (p != inp) {
    
    			/* copy any charachters to the result string */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			len = p - inp;
    			res_str = xrealloc(res_str, (res_str_len + len));
    			strncpy((res_str + res_str_len), inp, len);
    			res_str_len += len;
    		}
    		inp = ++p;
    
    		/* find the ending marker */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		p = strchr(inp, SPECIAL_VAR_SYMBOL);
    		*p = '\0';
    
    		/* look up the value to substitute */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if ((p1 = lookup_param(inp))) {
    
    			if (tag_subst)
    				len = res_str_len + strlen(p1) + 2;
    			else
    				len = res_str_len + strlen(p1);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			res_str = xrealloc(res_str, (1 + len));
    
    			if (tag_subst) {
    				/*
    				 * copy the variable value to the result
    				 * string
    				 */
    				strcpy((res_str + res_str_len + 1), p1);
    
    				/*
    				 * mark the replaced text to be accepted as
    				 * is
    				 */
    				res_str[res_str_len] = SUBSTED_VAR_SYMBOL;
    				res_str[res_str_len + 1 + strlen(p1)] =
    					SUBSTED_VAR_SYMBOL;
    			} else
    				/*
    				 * copy the variable value to the result
    				 * string
    				 */
    				strcpy((res_str + res_str_len), p1);
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			res_str_len = len;
    		}
    		*p = SPECIAL_VAR_SYMBOL;
    		inp = ++p;
    		done = 1;
    	}
    	if (done) {
    		res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp)));
    		strcpy((res_str + res_str_len), inp);
    		while ((p = strchr(res_str, '\n'))) {
    			*p = ' ';
    		}
    	}
    	return (res_str == NULL) ? inp : res_str;
    }
    
    static char **make_list_in(char **inp, char *name)
    {
    	int len, i;
    	int name_len = strlen(name);
    	int n = 0;
    	char **list;
    	char *p1, *p2, *p3;
    
    	/* create list of variable values */
    	list = xmalloc(sizeof(*list));
    	for (i = 0; inp[i]; i++) {
    		p3 = insert_var_value(inp[i]);
    		p1 = p3;
    		while (*p1) {
    			if ((*p1 == ' ')) {
    				p1++;
    				continue;
    			}
    			if ((p2 = strchr(p1, ' '))) {
    				len = p2 - p1;
    			} else {
    				len = strlen(p1);
    				p2 = p1 + len;
    			}
    			/* we use n + 2 in realloc for list,because we add
    			 * new element and then we will add NULL element */
    			list = xrealloc(list, sizeof(*list) * (n + 2));
    			list[n] = xmalloc(2 + name_len + len);
    			strcpy(list[n], name);
    			strcat(list[n], "=");
    			strncat(list[n], p1, len);
    			list[n++][name_len + len + 1] = '\0';
    			p1 = p2;
    		}
    		if (p3 != inp[i]) free(p3);
    	}
    	list[n] = NULL;
    	return list;
    }
    
    
    /*
     * Make new string for parser
     * inp     - array of argument strings to flatten
     * nonnull - indicates argument was quoted when originally parsed
     */
    static char *make_string(char **inp, int *nonnull)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	char *p;
    	char *str = NULL;
    	int n;
    	int len = 2;
    
    	char *noeval_str;
    	int noeval = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	noeval_str = get_local_var("HUSH_NO_EVAL");
    	if (noeval_str != NULL && *noeval_str != '0' && *noeval_str != '\0')
    		noeval = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	for (n = 0; inp[n]; n++) {
    
    		p = insert_var_value_sub(inp[n], noeval);
    
    		str = xrealloc(str, (len + strlen(p) + (2 * nonnull[n])));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (n) {
    			strcat(str, " ");
    		} else {
    			*str = '\0';
    		}
    
    		if (nonnull[n])
    			strcat(str, "'");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		strcat(str, p);
    
    		if (nonnull[n])
    			strcat(str, "'");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		len = strlen(str) + 3;
    		if (p != inp[n]) free(p);
    	}
    	len = strlen(str);
    	*(str + len) = '\n';
    	*(str + len + 1) = '\0';
    	return str;
    }
    
    
    #ifdef __U_BOOT__
    
    Kim Phillips's avatar
    Kim Phillips committed
    static int do_showvar(cmd_tbl_t *cmdtp, int flag, int argc,
    		      char * const argv[])
    
    {
    	int i, k;
    	int rcode = 0;
    	struct variables *cur;
    
    	if (argc == 1) {		/* Print all env variables	*/
    		for (cur = top_vars; cur; cur = cur->next) {
    			printf ("%s=%s\n", cur->name, cur->value);
    			if (ctrlc ()) {
    				puts ("\n ** Abort\n");
    				return 1;
    			}
    		}
    		return 0;
    	}
    	for (i = 1; i < argc; ++i) {	/* print single env variables	*/
    		char *name = argv[i];
    
    		k = -1;
    		for (cur = top_vars; cur; cur = cur->next) {
    			if(strcmp (cur->name, name) == 0) {
    				k = 0;
    				printf ("%s=%s\n", cur->name, cur->value);
    			}
    			if (ctrlc ()) {
    				puts ("\n ** Abort\n");
    				return 1;
    			}
    		}
    		if (k < 0) {
    			printf ("## Error: \"%s\" not defined\n", name);
    			rcode ++;
    		}
    	}
    	return rcode;
    }
    
    U_BOOT_CMD(
    
    	showvar, CONFIG_SYS_MAXARGS, 1,	do_showvar,
    
    Peter Tyser's avatar
    Peter Tyser committed
    	"print local hushshell variables",
    
    	"\n    - print values of all hushshell variables\n"
    	"showvar name ...\n"
    
    	"    - print value of hushshell variable 'name'"
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /****************************************************************************/