hush: fix job control with eval /bin/external_prog
hush: fix parsing of unterminated "str with no EOL hush: improved make_string() (smaller, faster, needs less RAM) hush: renamed several functions
This commit is contained in:
		
							
								
								
									
										171
									
								
								shell/hush.c
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								shell/hush.c
									
									
									
									
									
								
							| @@ -117,6 +117,9 @@ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ | |||||||
| #define DEBUG_EXPAND 1 | #define DEBUG_EXPAND 1 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | /* Keep unconditionally on for now */ | ||||||
|  | #define ENABLE_HUSH_DEBUG 1 | ||||||
|  |  | ||||||
| #ifndef debug_printf_clean | #ifndef debug_printf_clean | ||||||
| /* broken, of course, but OK for testing */ | /* broken, of course, but OK for testing */ | ||||||
| static const char *indenter(int i) | static const char *indenter(int i) | ||||||
| @@ -497,13 +500,12 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, struct in | |||||||
| #endif | #endif | ||||||
| static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); | static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); | ||||||
| static const char *lookup_param(const char *src); | static const char *lookup_param(const char *src); | ||||||
| static char *make_string(char **inp); |  | ||||||
| static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); | static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); | ||||||
| static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger); | static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger); | ||||||
| /*   setup: */ | /*   setup: */ | ||||||
| static int parse_stream_outer(struct in_str *inp, int parse_flag); | static int parse_and_run_stream(struct in_str *inp, int parse_flag); | ||||||
| static int parse_string_outer(const char *s, int parse_flag); | static int parse_and_run_string(const char *s, int parse_flag); | ||||||
| static int parse_file_outer(FILE *f); | static int parse_and_run_file(FILE *f); | ||||||
| /*   job management: */ | /*   job management: */ | ||||||
| static int checkjobs(struct pipe* fg_pipe); | static int checkjobs(struct pipe* fg_pipe); | ||||||
| #if ENABLE_HUSH_JOB | #if ENABLE_HUSH_JOB | ||||||
| @@ -515,9 +517,11 @@ static void delete_finished_bg_job(struct pipe *pi); | |||||||
| int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ | int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ | ||||||
| #endif | #endif | ||||||
| /*     local variable support */ | /*     local variable support */ | ||||||
| static char **expand_variables_to_list(char **argv); | static char **expand_strvec_to_strvec(char **argv); | ||||||
|  | /* used for eval */ | ||||||
|  | static char *expand_strvec_to_string(char **argv); | ||||||
| /* used for expansion of right hand of assignments */ | /* used for expansion of right hand of assignments */ | ||||||
| static char *expand_variables_to_string(const char *str); | static char *expand_string_to_string(const char *str); | ||||||
| static const char *get_local_var(const char *var); | static const char *get_local_var(const char *var); | ||||||
| static int set_local_var(const char *s, int flg_export); | static int set_local_var(const char *s, int flg_export); | ||||||
| static void unset_local_var(const char *name); | static void unset_local_var(const char *name); | ||||||
| @@ -716,12 +720,11 @@ static const char *set_cwd(void) | |||||||
| /* built-in 'eval' handler */ | /* built-in 'eval' handler */ | ||||||
| static int builtin_eval(char **argv) | static int builtin_eval(char **argv) | ||||||
| { | { | ||||||
| 	char *str = NULL; |  | ||||||
| 	int rcode = EXIT_SUCCESS; | 	int rcode = EXIT_SUCCESS; | ||||||
|  |  | ||||||
| 	if (argv[1]) { | 	if (argv[1]) { | ||||||
| 		str = make_string(argv + 1); | 		char *str = expand_strvec_to_string(argv + 1); | ||||||
| 		parse_string_outer(str, PARSEFLAG_EXIT_FROM_LOOP | | 		parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP | | ||||||
| 					PARSEFLAG_SEMICOLON); | 					PARSEFLAG_SEMICOLON); | ||||||
| 		free(str); | 		free(str); | ||||||
| 		rcode = last_return_code; | 		rcode = last_return_code; | ||||||
| @@ -732,9 +735,9 @@ static int builtin_eval(char **argv) | |||||||
| /* built-in 'cd <path>' handler */ | /* built-in 'cd <path>' handler */ | ||||||
| static int builtin_cd(char **argv) | static int builtin_cd(char **argv) | ||||||
| { | { | ||||||
| 	char *newdir; | 	const char *newdir; | ||||||
| 	if (argv[1] == NULL) | 	if (argv[1] == NULL) | ||||||
| 		newdir = getenv("HOME"); | 		newdir = getenv("HOME") ? : "/"; | ||||||
| 	else | 	else | ||||||
| 		newdir = argv[1]; | 		newdir = argv[1]; | ||||||
| 	if (chdir(newdir)) { | 	if (chdir(newdir)) { | ||||||
| @@ -999,7 +1002,7 @@ static int builtin_source(char **argv) | |||||||
| 	 * (pointer only is OK!) on this stack frame, | 	 * (pointer only is OK!) on this stack frame, | ||||||
| 	 * set global_argv=argv+1, recurse, and restore. */ | 	 * set global_argv=argv+1, recurse, and restore. */ | ||||||
| 	mark_open(fileno(input)); | 	mark_open(fileno(input)); | ||||||
| 	status = parse_file_outer(input); | 	status = parse_and_run_file(input); | ||||||
| 	mark_closed(fileno(input)); | 	mark_closed(fileno(input)); | ||||||
| 	fclose(input); | 	fclose(input); | ||||||
| 	return status; | 	return status; | ||||||
| @@ -1146,12 +1149,10 @@ static void get_user_input(struct in_str *i) | |||||||
|  |  | ||||||
| 	prompt_str = setup_prompt_string(i->promptmode); | 	prompt_str = setup_prompt_string(i->promptmode); | ||||||
| #if ENABLE_FEATURE_EDITING | #if ENABLE_FEATURE_EDITING | ||||||
| 	/* | 	/* Enable command line editing only while a command line | ||||||
| 	 ** enable command line editing only while a command line | 	 * is actually being read; otherwise, we'll end up bequeathing | ||||||
| 	 ** is actually being read; otherwise, we'll end up bequeathing | 	 * atexit() handlers and other unwanted stuff to our | ||||||
| 	 ** atexit() handlers and other unwanted stuff to our | 	 * child processes (rob@sysgo.de) */ | ||||||
| 	 ** child processes (rob@sysgo.de) |  | ||||||
| 	 */ |  | ||||||
| 	r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state); | 	r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state); | ||||||
| 	i->eof_flag = (r < 0); | 	i->eof_flag = (r < 0); | ||||||
| 	if (i->eof_flag) { /* EOF/error detected */ | 	if (i->eof_flag) { /* EOF/error detected */ | ||||||
| @@ -1343,8 +1344,8 @@ static void pseudo_exec_argv(char **argv) | |||||||
| 		debug_printf_exec("pid %d environment modification: %s\n", | 		debug_printf_exec("pid %d environment modification: %s\n", | ||||||
| 				getpid(), argv[i]); | 				getpid(), argv[i]); | ||||||
| // FIXME: vfork case?? | // FIXME: vfork case?? | ||||||
| 		p = expand_variables_to_string(argv[i]); | 		p = expand_string_to_string(argv[i]); | ||||||
| 		putenv(p == argv[i] ? xstrdup(p) : p); | 		putenv(p); | ||||||
| 	} | 	} | ||||||
| 	argv += i; | 	argv += i; | ||||||
| 	/* If a variable is assigned in a forest, and nobody listens, | 	/* If a variable is assigned in a forest, and nobody listens, | ||||||
| @@ -1354,7 +1355,7 @@ static void pseudo_exec_argv(char **argv) | |||||||
| 		_exit(EXIT_SUCCESS); | 		_exit(EXIT_SUCCESS); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	argv = expand_variables_to_list(argv); | 	argv = expand_strvec_to_strvec(argv); | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * Check if the command matches any of the builtins. | 	 * Check if the command matches any of the builtins. | ||||||
| @@ -1652,8 +1653,8 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||||||
| 	pid_t p; | 	pid_t p; | ||||||
| 	int rcode = checkjobs(fg_pipe); | 	int rcode = checkjobs(fg_pipe); | ||||||
| 	/* Job finished, move the shell to the foreground */ | 	/* Job finished, move the shell to the foreground */ | ||||||
| 	p = getpgid(0); | 	p = getpgid(0); /* pgid of our process */ | ||||||
| 	debug_printf("fg'ing ourself: getpgid(0)=%d\n", (int)p); | 	debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); | ||||||
| 	if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY) | 	if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY) | ||||||
| 		bb_perror_msg("tcsetpgrp-4a"); | 		bb_perror_msg("tcsetpgrp-4a"); | ||||||
| 	return rcode; | 	return rcode; | ||||||
| @@ -1675,6 +1676,9 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||||||
|  * subshell, when that is in fact necessary.  The subshell process |  * subshell, when that is in fact necessary.  The subshell process | ||||||
|  * now has its stdout directed to the input of the appropriate pipe, |  * now has its stdout directed to the input of the appropriate pipe, | ||||||
|  * so this routine is noticeably simpler. |  * so this routine is noticeably simpler. | ||||||
|  |  * | ||||||
|  |  * Returns -1 only if started some children. IOW: we have to | ||||||
|  |  * mask out retvals of builtins etc with 0xff! | ||||||
|  */ |  */ | ||||||
| static int run_pipe_real(struct pipe *pi) | static int run_pipe_real(struct pipe *pi) | ||||||
| { | { | ||||||
| @@ -1710,7 +1714,7 @@ static int run_pipe_real(struct pipe *pi) | |||||||
| 		rcode = run_list_real(child->group); | 		rcode = run_list_real(child->group); | ||||||
| 		restore_redirects(squirrel); | 		restore_redirects(squirrel); | ||||||
| 		debug_printf_exec("run_pipe_real return %d\n", rcode); | 		debug_printf_exec("run_pipe_real return %d\n", rcode); | ||||||
| 		return rcode; | 		return rcode; // do we need to add '... & 0xff' ? | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (single_fg && child->argv != NULL) { | 	if (single_fg && child->argv != NULL) { | ||||||
| @@ -1739,21 +1743,16 @@ static int run_pipe_real(struct pipe *pi) | |||||||
| 					export_me = 1; | 					export_me = 1; | ||||||
| 				} | 				} | ||||||
| 				free(name); | 				free(name); | ||||||
| 				p = expand_variables_to_string(argv[i]); | 				p = expand_string_to_string(argv[i]); | ||||||
| 				set_local_var(p, export_me); | 				set_local_var(p, export_me); | ||||||
| 				if (p != argv[i]) |  | ||||||
| 				free(p); | 				free(p); | ||||||
| 			} | 			} | ||||||
| 			return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */ | 			return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */ | ||||||
| 		} | 		} | ||||||
| 		for (i = 0; is_assignment(argv[i]); i++) { | 		for (i = 0; is_assignment(argv[i]); i++) { | ||||||
| 			p = expand_variables_to_string(argv[i]); | 			p = expand_string_to_string(argv[i]); | ||||||
| 			if (p != argv[i]) { |  | ||||||
| 			//sp: child->sp--; | 			//sp: child->sp--; | ||||||
| 			putenv(p); | 			putenv(p); | ||||||
| 			} else { |  | ||||||
| 				putenv(xstrdup(p)); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		for (x = bltins; x->cmd; x++) { | 		for (x = bltins; x->cmd; x++) { | ||||||
| 			if (strcmp(argv[i], x->cmd) == 0) { | 			if (strcmp(argv[i], x->cmd) == 0) { | ||||||
| @@ -1770,8 +1769,8 @@ static int run_pipe_real(struct pipe *pi) | |||||||
| 				setup_redirects(child, squirrel); | 				setup_redirects(child, squirrel); | ||||||
| 				debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]); | 				debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]); | ||||||
| 				//sp: if (child->sp) /* btw we can do it unconditionally... */ | 				//sp: if (child->sp) /* btw we can do it unconditionally... */ | ||||||
| 				argv_expanded = expand_variables_to_list(argv + i); | 				argv_expanded = expand_strvec_to_strvec(argv + i); | ||||||
| 				rcode = x->function(argv_expanded); | 				rcode = x->function(argv_expanded) & 0xff; | ||||||
| 				free(argv_expanded); | 				free(argv_expanded); | ||||||
| 				restore_redirects(squirrel); | 				restore_redirects(squirrel); | ||||||
| 				debug_printf_exec("run_pipe_real return %d\n", rcode); | 				debug_printf_exec("run_pipe_real return %d\n", rcode); | ||||||
| @@ -1786,9 +1785,9 @@ static int run_pipe_real(struct pipe *pi) | |||||||
| 				save_nofork_data(&nofork_save); | 				save_nofork_data(&nofork_save); | ||||||
| 				argv_expanded = argv + i; | 				argv_expanded = argv + i; | ||||||
| 				//sp: if (child->sp) | 				//sp: if (child->sp) | ||||||
| 				argv_expanded = expand_variables_to_list(argv + i); | 				argv_expanded = expand_strvec_to_strvec(argv + i); | ||||||
| 				debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); | 				debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); | ||||||
| 				rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded); | 				rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff; | ||||||
| 				free(argv_expanded); | 				free(argv_expanded); | ||||||
| 				restore_redirects(squirrel); | 				restore_redirects(squirrel); | ||||||
| 				debug_printf_exec("run_pipe_real return %d\n", rcode); | 				debug_printf_exec("run_pipe_real return %d\n", rcode); | ||||||
| @@ -1832,7 +1831,7 @@ static int run_pipe_real(struct pipe *pi) | |||||||
| 			/* Every child adds itself to new process group | 			/* Every child adds itself to new process group | ||||||
| 			 * with pgid == pid of first child in pipe */ | 			 * with pgid == pid of first child in pipe */ | ||||||
| #if ENABLE_HUSH_JOB | #if ENABLE_HUSH_JOB | ||||||
| 			if (interactive_fd) { | 			if (run_list_level == 1 && interactive_fd) { | ||||||
| 				/* Don't do pgrp restore anymore on fatal signals */ | 				/* Don't do pgrp restore anymore on fatal signals */ | ||||||
| 				set_fatal_sighandler(SIG_DFL); | 				set_fatal_sighandler(SIG_DFL); | ||||||
| 				if (pi->pgrp < 0) /* true for 1st process only */ | 				if (pi->pgrp < 0) /* true for 1st process only */ | ||||||
| @@ -2078,7 +2077,7 @@ static int run_list_real(struct pipe *pi) | |||||||
| 				if (!pi->next->progs->argv) | 				if (!pi->next->progs->argv) | ||||||
| 					continue; | 					continue; | ||||||
| 				/* create list of variable values */ | 				/* create list of variable values */ | ||||||
| 				for_list = expand_variables_to_list(pi->next->progs->argv); | 				for_list = expand_strvec_to_strvec(pi->next->progs->argv); | ||||||
| 				for_lcur = for_list; | 				for_lcur = for_list; | ||||||
| 				for_varname = pi->progs->argv[0]; | 				for_varname = pi->progs->argv[0]; | ||||||
| 				pi->progs->argv[0] = NULL; | 				pi->progs->argv[0] = NULL; | ||||||
| @@ -2122,24 +2121,24 @@ static int run_list_real(struct pipe *pi) | |||||||
| 			 * of run_pipe_real(), and we don't need to wait for anything. */ | 			 * of run_pipe_real(), and we don't need to wait for anything. */ | ||||||
| 		} else if (pi->followup == PIPE_BG) { | 		} else if (pi->followup == PIPE_BG) { | ||||||
| 			/* What does bash do with attempts to background builtins? */ | 			/* What does bash do with attempts to background builtins? */ | ||||||
|  |  | ||||||
| 			/* Even bash 3.2 doesn't do that well with nested bg: | 			/* Even bash 3.2 doesn't do that well with nested bg: | ||||||
| 			 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 			 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | ||||||
| 			 * I'm considering NOT treating inner bgs as jobs - | 			 * I'm NOT treating inner &'s as jobs */ | ||||||
| 			 * thus maybe "if (run_list_level == 1 && pi->followup == PIPE_BG)" |  | ||||||
| 			 * above? */ |  | ||||||
| #if ENABLE_HUSH_JOB | #if ENABLE_HUSH_JOB | ||||||
|  | 			if (run_list_level == 1)  | ||||||
| 				insert_bg_job(pi); | 				insert_bg_job(pi); | ||||||
| #endif | #endif | ||||||
| 			rcode = EXIT_SUCCESS; | 			rcode = EXIT_SUCCESS; | ||||||
| 		} else { | 		} else { | ||||||
| #if ENABLE_HUSH_JOB | #if ENABLE_HUSH_JOB | ||||||
| 			/* Paranoia, just "interactive_fd" should be enough */ | 			/* Paranoia, just "interactive_fd" should be enough? */ | ||||||
| 			if (run_list_level == 1 && interactive_fd) { | 			if (run_list_level == 1 && interactive_fd) { | ||||||
|  | 				/* waits for completion, then fg's main shell */ | ||||||
| 				rcode = checkjobs_and_fg_shell(pi); | 				rcode = checkjobs_and_fg_shell(pi); | ||||||
| 			} else | 			} else | ||||||
| #endif | #endif | ||||||
| 			{ | 			{ | ||||||
|  | 				/* this one just waits for completion */ | ||||||
| 				rcode = checkjobs(pi); | 				rcode = checkjobs(pi); | ||||||
| 			} | 			} | ||||||
| 			debug_printf_exec(": checkjobs returned %d\n", rcode); | 			debug_printf_exec(": checkjobs returned %d\n", rcode); | ||||||
| @@ -2343,7 +2342,7 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) | |||||||
| 	return gr; | 	return gr; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* expand_variables_to_list() takes a list of strings, expands | /* expand_strvec_to_strvec() takes a list of strings, expands | ||||||
|  * all variable references within and returns a pointer to |  * all variable references within and returns a pointer to | ||||||
|  * a list of expanded strings, possibly with larger number |  * a list of expanded strings, possibly with larger number | ||||||
|  * of strings. (Think VAR="a b"; echo $VAR). |  * of strings. (Think VAR="a b"; echo $VAR). | ||||||
| @@ -2629,29 +2628,51 @@ static char **expand_variables(char **argv, char or_mask) | |||||||
| 		debug_printf_expand("used_space=%d\n", pos - (char*)list); | 		debug_printf_expand("used_space=%d\n", pos - (char*)list); | ||||||
| 	} | 	} | ||||||
| #endif | #endif | ||||||
| 	/* To be removed / made conditional later. */ | 	if (ENABLE_HUSH_DEBUG) | ||||||
| 		if (pos - (char*)list > len) | 		if (pos - (char*)list > len) | ||||||
| 			bb_error_msg_and_die("BUG in varexp"); | 			bb_error_msg_and_die("BUG in varexp"); | ||||||
| 	return list; | 	return list; | ||||||
| } | } | ||||||
|  |  | ||||||
| static char **expand_variables_to_list(char **argv) | static char **expand_strvec_to_strvec(char **argv) | ||||||
| { | { | ||||||
| 	return expand_variables(argv, 0); | 	return expand_variables(argv, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
| static char *expand_variables_to_string(const char *str) | static char *expand_string_to_string(const char *str) | ||||||
| { | { | ||||||
| 	char *argv[2], **list; | 	char *argv[2], **list; | ||||||
|  |  | ||||||
| 	argv[0] = (char*)str; | 	argv[0] = (char*)str; | ||||||
| 	argv[1] = NULL; | 	argv[1] = NULL; | ||||||
| 	list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ | 	list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ | ||||||
| 	/* To be removed / made conditional later. */ | 	if (ENABLE_HUSH_DEBUG) | ||||||
| 		if (!list[0] || list[1]) | 		if (!list[0] || list[1]) | ||||||
| 		bb_error_msg_and_die("BUG in varexp"); | 			bb_error_msg_and_die("BUG in varexp2"); | ||||||
| 	/* actually, just move string 2*sizeof(char*) bytes back */ | 	/* actually, just move string 2*sizeof(char*) bytes back */ | ||||||
| 	strcpy((char*)list, list[0]); | 	strcpy((char*)list, list[0]); | ||||||
|  | 	debug_printf_expand("string_to_string='%s'\n", (char*)list); | ||||||
|  | 	return (char*)list; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static char* expand_strvec_to_string(char **argv) | ||||||
|  | { | ||||||
|  | 	char **list; | ||||||
|  |  | ||||||
|  | 	list = expand_variables(argv, 0x80); | ||||||
|  | 	/* Convert all NULs to spaces */ | ||||||
|  | 	if (list[0]) { | ||||||
|  | 		int n = 1; | ||||||
|  | 		while (list[n]) { | ||||||
|  | 			if (ENABLE_HUSH_DEBUG) | ||||||
|  | 				if (list[n-1] + strlen(list[n-1]) + 1 != list[n]) | ||||||
|  | 					bb_error_msg_and_die("BUG in varexp3"); | ||||||
|  | 			list[n][-1] = ' '; /* TODO: or to ifs[0]? */ | ||||||
|  | 			n++; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	strcpy((char*)list, list[0]); | ||||||
|  | 	debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | ||||||
| 	return (char*)list; | 	return (char*)list; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2713,9 +2734,9 @@ static int set_local_var(const char *s, int flg_export) | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cur = xzalloc(sizeof(*cur)); | 	cur = xzalloc(sizeof(*cur)); | ||||||
|  | 	/*cur->next = 0;*/ | ||||||
| 	cur->name = xstrdup(name); | 	cur->name = xstrdup(name); | ||||||
| 	cur->value = xstrdup(value); | 	cur->value = xstrdup(value); | ||||||
| 	/*cur->next = 0;*/ |  | ||||||
| 	cur->flg_export = flg_export; | 	cur->flg_export = flg_export; | ||||||
| 	/*cur->flg_read_only = 0;*/ | 	/*cur->flg_read_only = 0;*/ | ||||||
| 	{ | 	{ | ||||||
| @@ -3242,31 +3263,6 @@ static const char *lookup_param(const char *src) | |||||||
| 	return p; | 	return p; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Make new string for parser */ |  | ||||||
| static char* make_string(char **inp) |  | ||||||
| { |  | ||||||
| 	char *p; |  | ||||||
| 	char *str = NULL; |  | ||||||
| 	int n; |  | ||||||
| 	int val_len; |  | ||||||
| 	int len = 0; |  | ||||||
|  |  | ||||||
| 	for (n = 0; inp[n]; n++) { |  | ||||||
| 		p = expand_variables_to_string(inp[n]); |  | ||||||
| 		val_len = strlen(p); |  | ||||||
| 		str = xrealloc(str, len + val_len + 3); /* +3: space, '\n', <nul>*/ |  | ||||||
| 		str[len++] = ' '; |  | ||||||
| 		strcpy(str + len, p); |  | ||||||
| 		len += val_len; |  | ||||||
| 		if (p != inp[n]) free(p); |  | ||||||
| 	} |  | ||||||
| 	/* We do not check for case where loop had no iterations at all |  | ||||||
| 	 * - cannot happen? */ |  | ||||||
| 	str[len] = '\n'; |  | ||||||
| 	str[len+1] = '\0'; |  | ||||||
| 	return str; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* return code: 0 for OK, 1 for syntax error */ | /* return code: 0 for OK, 1 for syntax error */ | ||||||
| static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) | static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) | ||||||
| { | { | ||||||
| @@ -3358,9 +3354,9 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||||||
| 	debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger); | 	debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger); | ||||||
|  |  | ||||||
| 	while (1) { | 	while (1) { | ||||||
| 		ch = b_getch(input); |  | ||||||
| 		m = CHAR_IFS; | 		m = CHAR_IFS; | ||||||
| 		next = '\0'; | 		next = '\0'; | ||||||
|  | 		ch = b_getch(input); | ||||||
| 		if (ch != EOF) { | 		if (ch != EOF) { | ||||||
| 			m = charmap[ch]; | 			m = charmap[ch]; | ||||||
| 			if (ch != '\n') | 			if (ch != '\n') | ||||||
| @@ -3371,6 +3367,11 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||||||
| 		if (m == CHAR_ORDINARY | 		if (m == CHAR_ORDINARY | ||||||
| 		 || (m != CHAR_SPECIAL && dest->quote) | 		 || (m != CHAR_SPECIAL && dest->quote) | ||||||
| 		) { | 		) { | ||||||
|  | 			if (ch == EOF) { | ||||||
|  | 				syntax(); | ||||||
|  | 				debug_printf_parse("parse_stream return 1: unterminated \"\n"); | ||||||
|  | 				return 1; | ||||||
|  | 			} | ||||||
| 			b_addqchr(dest, ch, dest->quote); | 			b_addqchr(dest, ch, dest->quote); | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| @@ -3564,8 +3565,8 @@ static void update_charmap(void) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* most recursion does not come through here, the exception is | /* most recursion does not come through here, the exception is | ||||||
|  * from builtin_source() */ |  * from builtin_source() and builtin_eval() */ | ||||||
| static int parse_stream_outer(struct in_str *inp, int parse_flag) | static int parse_and_run_stream(struct in_str *inp, int parse_flag) | ||||||
| { | { | ||||||
| 	struct p_context ctx; | 	struct p_context ctx; | ||||||
| 	o_string temp = NULL_O_STRING; | 	o_string temp = NULL_O_STRING; | ||||||
| @@ -3607,19 +3608,19 @@ static int parse_stream_outer(struct in_str *inp, int parse_flag) | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parse_string_outer(const char *s, int parse_flag) | static int parse_and_run_string(const char *s, int parse_flag) | ||||||
| { | { | ||||||
| 	struct in_str input; | 	struct in_str input; | ||||||
| 	setup_string_in_str(&input, s); | 	setup_string_in_str(&input, s); | ||||||
| 	return parse_stream_outer(&input, parse_flag); | 	return parse_and_run_stream(&input, parse_flag); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int parse_file_outer(FILE *f) | static int parse_and_run_file(FILE *f) | ||||||
| { | { | ||||||
| 	int rcode; | 	int rcode; | ||||||
| 	struct in_str input; | 	struct in_str input; | ||||||
| 	setup_file_in_str(&input, f); | 	setup_file_in_str(&input, f); | ||||||
| 	rcode = parse_stream_outer(&input, PARSEFLAG_SEMICOLON); | 	rcode = parse_and_run_stream(&input, PARSEFLAG_SEMICOLON); | ||||||
| 	return rcode; | 	return rcode; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -3698,7 +3699,7 @@ int hush_main(int argc, char **argv) | |||||||
| 		input = fopen("/etc/profile", "r"); | 		input = fopen("/etc/profile", "r"); | ||||||
| 		if (input != NULL) { | 		if (input != NULL) { | ||||||
| 			mark_open(fileno(input)); | 			mark_open(fileno(input)); | ||||||
| 			parse_file_outer(input); | 			parse_and_run_file(input); | ||||||
| 			mark_closed(fileno(input)); | 			mark_closed(fileno(input)); | ||||||
| 			fclose(input); | 			fclose(input); | ||||||
| 		} | 		} | ||||||
| @@ -3710,7 +3711,7 @@ int hush_main(int argc, char **argv) | |||||||
| 		case 'c': | 		case 'c': | ||||||
| 			global_argv = argv + optind; | 			global_argv = argv + optind; | ||||||
| 			global_argc = argc - optind; | 			global_argc = argc - optind; | ||||||
| 			opt = parse_string_outer(optarg, PARSEFLAG_SEMICOLON); | 			opt = parse_and_run_string(optarg, PARSEFLAG_SEMICOLON); | ||||||
| 			goto final_return; | 			goto final_return; | ||||||
| 		case 'i': | 		case 'i': | ||||||
| 			/* Well, we cannot just declare interactiveness, | 			/* Well, we cannot just declare interactiveness, | ||||||
| @@ -3791,7 +3792,7 @@ int hush_main(int argc, char **argv) | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 	if (argv[optind] == NULL) { | 	if (argv[optind] == NULL) { | ||||||
| 		opt = parse_file_outer(stdin); | 		opt = parse_and_run_file(stdin); | ||||||
| 		goto final_return; | 		goto final_return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -3799,7 +3800,7 @@ int hush_main(int argc, char **argv) | |||||||
| 	global_argv = argv + optind; | 	global_argv = argv + optind; | ||||||
| 	global_argc = argc - optind; | 	global_argc = argc - optind; | ||||||
| 	input = xfopen(argv[optind], "r"); | 	input = xfopen(argv[optind], "r"); | ||||||
| 	opt = parse_file_outer(input); | 	opt = parse_and_run_file(input); | ||||||
|  |  | ||||||
| #if ENABLE_FEATURE_CLEAN_UP | #if ENABLE_FEATURE_CLEAN_UP | ||||||
| 	fclose(input); | 	fclose(input); | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								shell/hush_test/hush-bugs/noeol3.right
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								shell/hush_test/hush-bugs/noeol3.right
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | hush: syntax error hush.c:3370 | ||||||
							
								
								
									
										2
									
								
								shell/hush_test/hush-bugs/noeol3.tests
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										2
									
								
								shell/hush_test/hush-bugs/noeol3.tests
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | # last line has no EOL! | ||||||
|  | echo "unterminated | ||||||
		Reference in New Issue
	
	Block a user