hush: fix incorrect processing of echo "'$var'".
hush: rename map[] and MAP_xxx, making them easier to understand. hush: move testcase (which now passes) from hush-bugs to hush-parsing
This commit is contained in:
		
							
								
								
									
										50
									
								
								shell/hush.c
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								shell/hush.c
									
									
									
									
									
								
							@@ -71,7 +71,7 @@
 | 
				
			|||||||
 *      memory leak finding and plugging - done?
 | 
					 *      memory leak finding and plugging - done?
 | 
				
			||||||
 *      more testing, especially quoting rules and redirection
 | 
					 *      more testing, especially quoting rules and redirection
 | 
				
			||||||
 *      document how quoting rules not precisely followed for variable assignments
 | 
					 *      document how quoting rules not precisely followed for variable assignments
 | 
				
			||||||
 *      maybe change map[] to use 2-bit entries
 | 
					 *      maybe change charmap[] to use 2-bit entries
 | 
				
			||||||
 *      (eventually) remove all the printf's
 | 
					 *      (eventually) remove all the printf's
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
 | 
					 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
 | 
				
			||||||
@@ -281,12 +281,12 @@ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* "globals" within this file */
 | 
					/* "globals" within this file */
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	MAP_ORDINARY             = 0,
 | 
						CHAR_ORDINARY           = 0,
 | 
				
			||||||
	MAP_FLOWTROUGH_IF_QUOTED = 1,
 | 
						CHAR_ORDINARY_IF_QUOTED = 1, /* example: *, # */
 | 
				
			||||||
	MAP_IFS_IF_UNQUOTED      = 2, /* flow through if quoted too */
 | 
						CHAR_IFS                = 2, /* treated as ordinary if quoted */
 | 
				
			||||||
	MAP_NEVER_FLOWTROUGH     = 3,
 | 
						CHAR_SPECIAL            = 3, /* example: $ */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
static unsigned char map[256];
 | 
					static unsigned char charmap[256];
 | 
				
			||||||
static const char *ifs;
 | 
					static const char *ifs;
 | 
				
			||||||
static int fake_mode;
 | 
					static int fake_mode;
 | 
				
			||||||
static struct close_me *close_me_head;
 | 
					static struct close_me *close_me_head;
 | 
				
			||||||
@@ -2311,7 +2311,7 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
 | 
				
			|||||||
 * Caller can deallocate entire list by single free(list). */
 | 
					 * Caller can deallocate entire list by single free(list). */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Helpers first:
 | 
					/* Helpers first:
 | 
				
			||||||
 * count_XXX estimates, how large block do we need. It's okay
 | 
					 * count_XXX estimates size of the block we need. It's okay
 | 
				
			||||||
 * to over-estimate sizes a bit, if it makes code simpler */
 | 
					 * to over-estimate sizes a bit, if it makes code simpler */
 | 
				
			||||||
static int count_ifs(const char *str)
 | 
					static int count_ifs(const char *str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -3360,17 +3360,17 @@ 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 ((ch = b_getch(input)) != EOF) {
 | 
						while ((ch = b_getch(input)) != EOF) {
 | 
				
			||||||
		m = map[ch];
 | 
							m = charmap[ch];
 | 
				
			||||||
		next = (ch == '\n') ? '\0' : b_peek(input);
 | 
							next = (ch == '\n') ? '\0' : b_peek(input);
 | 
				
			||||||
		debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
 | 
							debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
 | 
				
			||||||
						ch, ch, m, dest->quote);
 | 
											ch, ch, m, dest->quote);
 | 
				
			||||||
		if (m == MAP_ORDINARY
 | 
							if (m == CHAR_ORDINARY
 | 
				
			||||||
		 || (m != MAP_NEVER_FLOWTROUGH && dest->quote)
 | 
							 || (m != CHAR_SPECIAL && dest->quote)
 | 
				
			||||||
		) {
 | 
							) {
 | 
				
			||||||
			b_addqchr(dest, ch, dest->quote);
 | 
								b_addqchr(dest, ch, dest->quote);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (m == MAP_IFS_IF_UNQUOTED) {
 | 
							if (m == CHAR_IFS) {
 | 
				
			||||||
			if (done_word(dest, ctx)) {
 | 
								if (done_word(dest, ctx)) {
 | 
				
			||||||
				debug_printf_parse("parse_stream return 1: done_word!=0\n");
 | 
									debug_printf_parse("parse_stream return 1: done_word!=0\n");
 | 
				
			||||||
				return 1;
 | 
									return 1;
 | 
				
			||||||
@@ -3388,7 +3388,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
 | 
				
			|||||||
			debug_printf_parse("parse_stream return 0: end_trigger char found\n");
 | 
								debug_printf_parse("parse_stream return 0: end_trigger char found\n");
 | 
				
			||||||
			return 0;
 | 
								return 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (m == MAP_IFS_IF_UNQUOTED)
 | 
							if (m == CHAR_IFS)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		switch (ch) {
 | 
							switch (ch) {
 | 
				
			||||||
		case '#':
 | 
							case '#':
 | 
				
			||||||
@@ -3527,28 +3527,28 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void mapset(const char *set, int code)
 | 
					static void set_in_charmap(const char *set, int code)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	while (*set)
 | 
						while (*set)
 | 
				
			||||||
		map[(unsigned char)*set++] = code;
 | 
							charmap[(unsigned char)*set++] = code;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void update_ifs_map(void)
 | 
					static void update_charmap(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* char *ifs and char map[256] are both globals. */
 | 
						/* char *ifs and char charmap[256] are both globals. */
 | 
				
			||||||
	ifs = getenv("IFS");
 | 
						ifs = getenv("IFS");
 | 
				
			||||||
	if (ifs == NULL)
 | 
						if (ifs == NULL)
 | 
				
			||||||
		ifs = " \t\n";
 | 
							ifs = " \t\n";
 | 
				
			||||||
	/* Precompute a list of 'flow through' behavior so it can be treated
 | 
						/* Precompute a list of 'flow through' behavior so it can be treated
 | 
				
			||||||
	 * quickly up front.  Computation is necessary because of IFS.
 | 
						 * quickly up front.  Computation is necessary because of IFS.
 | 
				
			||||||
	 * Special case handling of IFS == " \t\n" is not implemented.
 | 
						 * Special case handling of IFS == " \t\n" is not implemented.
 | 
				
			||||||
	 * The map[] array only really needs two bits each, and on most machines
 | 
						 * The charmap[] array only really needs two bits each,
 | 
				
			||||||
	 * that would be faster because of the reduced L1 cache footprint.
 | 
						 * and on most machines that would be faster (reduced L1 cache use).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	memset(map, MAP_ORDINARY, sizeof(map)); /* most chars flow through always */
 | 
						memset(charmap, CHAR_ORDINARY, sizeof(charmap));
 | 
				
			||||||
	mapset("\\$'\"`", MAP_NEVER_FLOWTROUGH);
 | 
						set_in_charmap("\\$\"`", CHAR_SPECIAL);
 | 
				
			||||||
	mapset("<>;&|(){}#", MAP_FLOWTROUGH_IF_QUOTED);
 | 
						set_in_charmap("<>;&|(){}#'", CHAR_ORDINARY_IF_QUOTED);
 | 
				
			||||||
	mapset(ifs, MAP_IFS_IF_UNQUOTED);  /* also flow through if quoted */
 | 
						set_in_charmap(ifs, CHAR_IFS);  /* also flow through if quoted */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* most recursion does not come through here, the exception is
 | 
					/* most recursion does not come through here, the exception is
 | 
				
			||||||
@@ -3561,9 +3561,9 @@ static int parse_stream_outer(struct in_str *inp, int parse_flag)
 | 
				
			|||||||
	do {
 | 
						do {
 | 
				
			||||||
		ctx.parse_type = parse_flag;
 | 
							ctx.parse_type = parse_flag;
 | 
				
			||||||
		initialize_context(&ctx);
 | 
							initialize_context(&ctx);
 | 
				
			||||||
		update_ifs_map();
 | 
							update_charmap();
 | 
				
			||||||
		if (!(parse_flag & FLAG_PARSE_SEMICOLON) || (parse_flag & FLAG_REPARSING))
 | 
							if (!(parse_flag & FLAG_PARSE_SEMICOLON) || (parse_flag & FLAG_REPARSING))
 | 
				
			||||||
			mapset(";$&|", MAP_ORDINARY);
 | 
								set_in_charmap(";$&|", CHAR_ORDINARY);
 | 
				
			||||||
#if ENABLE_HUSH_INTERACTIVE
 | 
					#if ENABLE_HUSH_INTERACTIVE
 | 
				
			||||||
		inp->promptmode = 1;
 | 
							inp->promptmode = 1;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -3665,7 +3665,7 @@ int hush_main(int argc, char **argv)
 | 
				
			|||||||
	 * hush_main(), therefore we cannot rely on the BSS to zero out this
 | 
						 * hush_main(), therefore we cannot rely on the BSS to zero out this
 | 
				
			||||||
	 * stuff.  Reset these to 0 every time. */
 | 
						 * stuff.  Reset these to 0 every time. */
 | 
				
			||||||
	ifs = NULL;
 | 
						ifs = NULL;
 | 
				
			||||||
	/* map[] is taken care of with call to update_ifs_map() */
 | 
						/* charmap[] is taken care of with call to update_charmap() */
 | 
				
			||||||
	fake_mode = 0;
 | 
						fake_mode = 0;
 | 
				
			||||||
	close_me_head = NULL;
 | 
						close_me_head = NULL;
 | 
				
			||||||
#if ENABLE_HUSH_INTERACTIVE
 | 
					#if ENABLE_HUSH_INTERACTIVE
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user