BASH PATCH REPORT ================= Bash-Release: 4.0 Patch-ID: bash40-001 Bug-Reported-by: Mike Frysinger Bug-Reference-ID: <200902211821.42188.vapier@gentoo.org> Bug-Reference-URL: http://lists.gnu.org/archive/html/bug-bash/2009-02/msg00147.html Bug-Description: Bash has problems parsing certain constructs inside Posix-style $(...) command substitutions, mostly with backslash-quoting and reserved word recognition. This is an issue because the contents are parsed at the time the word containing the command substitution is read. Patch: *** ../bash-4.0/parse.y 2009-01-08 08:29:12.000000000 -0500 --- parse.y 2009-03-06 20:32:35.000000000 -0500 *************** *** 2928,2931 **** --- 2932,2936 ---- #define LEX_HEREDELIM 0x100 /* reading here-doc delimiter */ #define LEX_STRIPDOC 0x200 /* <<- strip tabs from here doc delim */ + #define LEX_INWORD 0x400 #define COMSUB_META(ch) ((ch) == ';' || (ch) == '&' || (ch) == '|') *************** *** 3180,3184 **** int *lenp, flags; { ! int count, ch, peekc, tflags, lex_rwlen, lex_firstind; int nestlen, ttranslen, start_lineno; char *ret, *nestret, *ttrans, *heredelim; --- 3188,3192 ---- int *lenp, flags; { ! int count, ch, peekc, tflags, lex_rwlen, lex_wlen, lex_firstind; int nestlen, ttranslen, start_lineno; char *ret, *nestret, *ttrans, *heredelim; *************** *** 3201,3205 **** start_lineno = line_number; ! lex_rwlen = 0; heredelim = 0; --- 3209,3213 ---- start_lineno = line_number; ! lex_rwlen = lex_wlen = 0; heredelim = 0; *************** *** 3268,3271 **** --- 3276,3319 ---- } + if (tflags & LEX_PASSNEXT) /* last char was backslash */ + { + /*itrace("parse_comsub:%d: lex_passnext -> 0 ch = `%c' (%d)", line_number, ch, __LINE__);*/ + tflags &= ~LEX_PASSNEXT; + if (qc != '\'' && ch == '\n') /* double-quoted \ disappears. */ + { + if (retind > 0) + retind--; /* swallow previously-added backslash */ + continue; + } + + RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); + if MBTEST(ch == CTLESC || ch == CTLNUL) + ret[retind++] = CTLESC; + ret[retind++] = ch; + continue; + } + + /* If this is a shell break character, we are not in a word. If not, + we either start or continue a word. */ + if MBTEST(shellbreak (ch)) + { + tflags &= ~LEX_INWORD; + /*itrace("parse_comsub:%d: lex_inword -> 0 ch = `%c' (%d)", line_number, ch, __LINE__);*/ + } + else + { + if (tflags & LEX_INWORD) + { + lex_wlen++; + /*itrace("parse_comsub:%d: lex_inword == 1 ch = `%c' lex_wlen = %d (%d)", line_number, ch, lex_wlen, __LINE__);*/ + } + else + { + /*itrace("parse_comsub:%d: lex_inword -> 1 ch = `%c' (%d)", line_number, ch, __LINE__);*/ + tflags |= LEX_INWORD; + lex_wlen = 0; + } + } + /* Skip whitespace */ if MBTEST(shellblank (ch) && lex_rwlen == 0) *************** *** 3400,3428 **** } else ! ch = peekc; /* fall through and continue XXX - this skips comments if peekc == '#' */ } ! /* Not exactly right yet, should handle shell metacharacters, too. If ! any changes are made to this test, make analogous changes to subst.c: ! extract_delimited_string(). */ ! else if MBTEST((tflags & LEX_CKCOMMENT) && (tflags & LEX_INCOMMENT) == 0 && ch == '#' && (retind == 0 || ret[retind-1] == '\n' || shellblank (ret[retind - 1]))) tflags |= LEX_INCOMMENT; ! if (tflags & LEX_PASSNEXT) /* last char was backslash */ ! { ! tflags &= ~LEX_PASSNEXT; ! if (qc != '\'' && ch == '\n') /* double-quoted \ disappears. */ ! { ! if (retind > 0) ! retind--; /* swallow previously-added backslash */ ! continue; ! } ! ! RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); ! if MBTEST(ch == CTLESC || ch == CTLNUL) ! ret[retind++] = CTLESC; ! ret[retind++] = ch; ! continue; ! } ! else if MBTEST(ch == CTLESC || ch == CTLNUL) /* special shell escapes */ { RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); --- 3442,3454 ---- } else ! ch = peekc; /* fall through and continue XXX */ } ! else if MBTEST((tflags & LEX_CKCOMMENT) && (tflags & LEX_INCOMMENT) == 0 && ch == '#' && (((tflags & LEX_RESWDOK) && lex_rwlen == 0) || ((tflags & LEX_INWORD) && lex_wlen == 0))) ! { ! /*itrace("parse_comsub:%d: lex_incomment -> 1 (%d)", line_number, __LINE__);*/ tflags |= LEX_INCOMMENT; + } ! if MBTEST(ch == CTLESC || ch == CTLNUL) /* special shell escapes */ { RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); *** ../bash-4.0/patchlevel.h 2009-01-04 14:32:40.000000000 -0500 --- patchlevel.h 2009-02-22 16:11:31.000000000 -0500 *************** *** 26,30 **** looks for to find the patch level (for the sccs version string). */ ! #define PATCHLEVEL 0 #endif /* _PATCHLEVEL_H_ */ --- 26,30 ---- looks for to find the patch level (for the sccs version string). */ ! #define PATCHLEVEL 1 #endif /* _PATCHLEVEL_H_ */