# builtin command. To avoid this problem, this function filters those
# variables out and discards them. See bug #190128.
filter_readonly_variables() {
- local x filtered_vars var_grep
+ local x filtered_vars
local readonly_bash_vars="DIRSTACK EUID FUNCNAME GROUPS
PIPESTATUS PPID SHELLOPTS UID"
local filtered_sandbox_vars="SANDBOX_ACTIVE SANDBOX_BASHRC
SANDBOX_DEBUG_LOG SANDBOX_DISABLED SANDBOX_LIB
SANDBOX_LOG SANDBOX_ON"
filtered_vars="${readonly_bash_vars} ${READONLY_PORTAGE_VARS}
- BASH_[_[:alnum:]]* PATH
- [[:digit:]][_[:alnum:]]*
- [^=[:space:]]*[^_[:alnum:]=[:space:]][^=[:space:]]*"
+ BASH_.* PATH"
if hasq --filter-sandbox $* ; then
- filtered_vars="${filtered_vars} SANDBOX_[_[:alnum:]]*"
+ filtered_vars="${filtered_vars} SANDBOX_.*"
else
filtered_vars="${filtered_vars} ${filtered_sandbox_vars}"
fi
${PORTAGE_MUTABLE_FILTERED_VARS}
"
fi
- set -f
- for x in ${filtered_vars} ; do
- var_grep="${var_grep}|${x}"
- done
- set +f
- var_grep=${var_grep:1} # strip the first |
- var_grep="(^|^declare[[:space:]]+-[^[:space:]]+[[:space:]]+|^export[[:space:]]+)(${var_grep})=.*"
- # The sed is to remove the readonly attribute from variables such as those
- # listed in READONLY_EBUILD_METADATA, since having any readonly attributes
- # persisting in the saved environment can be inconvenient when it
- # eventually needs to be reloaded.
- "${PORTAGE_BIN_PATH}"/filter-bash-environment.py "${var_grep}" | sed -r \
- -e 's:^declare[[:space:]]+-r[[:space:]]+:declare :' \
- -e 's:^declare[[:space:]]+-([[:alnum:]]*)r([[:alnum:]]*)[[:space:]]+:declare -\1\2 :'
+
+ "${PORTAGE_BIN_PATH}"/filter-bash-environment.py "${filtered_vars}"
}
# @FUNCTION: preprocess_ebuild_env
import os, re, sys
-egrep_compat_map = {
- "[:alnum:]" : r'\w',
- "[:digit:]" : r'\d',
- "[:space:]" : r'\s',
-}
-
here_doc_re = re.compile(r'.*\s<<[-]?(\w+)$')
+func_start_re = re.compile(r'^[-\w]+\s*\(\)\s*$')
+func_end_re = re.compile(r'^\}$')
+
+var_assign_re = re.compile(r'(^|^declare\s+-\S+\s+|^declare\s+|^export\s+)([^=\s]+)=("|\')?.*$')
+close_quote_re = re.compile(r'(\\"|"|\')\s*$')
+readonly_re = re.compile(r'^declare\s+-(\S*)r(\S*)\s+')
-def compile_egrep_pattern(s):
- for k, v in egrep_compat_map.iteritems():
- s = s.replace(k, v)
- return re.compile(s)
+def have_end_quote(quote, line):
+ """
+ Check if the line has an end quote (useful for handling multi-line
+ quotes). This handles escaped double quotes that may occur at the
+ end of a line. The posix spec does not allow escaping of single
+ quotes inside of single quotes, so that case is not handled.
+ """
+ close_quote_match = close_quote_re.search(line)
+ return close_quote_match is not None and \
+ close_quote_match.group(1) == quote
def filter_bash_environment(pattern, file_in, file_out):
here_doc_delim = None
+ in_func = None
+ multi_line_quote = None
+ multi_line_quote_filter = None
for line in file_in:
+ if multi_line_quote is not None:
+ if not multi_line_quote_filter:
+ file_out.write(line)
+ if have_end_quote(multi_line_quote, line):
+ multi_line_quote = None
+ multi_line_quote_filter = None
+ continue
+ if here_doc_delim is None and in_func is None:
+ var_assign_match = var_assign_re.match(line)
+ if var_assign_match is not None:
+ quote = var_assign_match.group(3)
+ filter_this = pattern.match(var_assign_match.group(2)) \
+ is not None
+ if quote is not None and not have_end_quote(quote, line):
+ multi_line_quote = quote
+ multi_line_quote_filter = filter_this
+ if not filter_this:
+ readonly_match = readonly_re.match(line)
+ if readonly_match is not None:
+ declare_opts = ""
+ for i in (1, 2):
+ group = readonly_match.group(i)
+ if group is not None:
+ declare_opts += group
+ if declare_opts:
+ line = "declare -%s %s" % \
+ (declare_opts, line[readonly_match.end():])
+ else:
+ line = "declare " + line[readonly_match.end():]
+ file_out.write(line)
+ continue
if here_doc_delim is not None:
if here_doc_delim.match(line):
here_doc_delim = None
here_doc_delim = re.compile("^%s$" % here_doc.group(1))
file_out.write(line)
continue
- if pattern.match(line) is None:
+ # Note: here-documents are handled before functions since otherwise
+ # it would be possible for the content of a here-document to be
+ # mistaken as the end of a function.
+ if in_func:
+ if func_end_re.match(line) is not None:
+ in_func = None
+ file_out.write(line)
+ continue
+ in_func = func_start_re.match(line)
+ if in_func is not None:
file_out.write(line)
+ continue
+ # This line is not recognized as part of a variable assignment,
+ # function definition, or here document, so just allow it to
+ # pass through.
+ file_out.write(line)
if __name__ == "__main__":
- description = "Filter out any lines that match a given PATTERN " + \
- "while leaving bash here-documents intact. The PATTERN should " + \
- "use python regular expression syntax but [:space:] and " + \
- "[:alnum:] character classes will be automatically translated " + \
- "for compatibility with egrep syntax."
+ description = "Filter out variable assignments for varable " + \
+ "names matching a given PATTERN " + \
+ "while leaving bash function definitions and here-documents " + \
+ "intact. The PATTERN is a space separated list of variable names" + \
+ " and it supports python regular expression syntax."
usage = "usage: %s PATTERN" % os.path.basename(sys.argv[0])
from optparse import OptionParser
parser = OptionParser(description=description, usage=usage)
parser.error("Missing required PATTERN argument.")
file_in = sys.stdin
file_out = sys.stdout
+ var_pattern = args[0].split()
+
+ # Filter invalid variable names that are not supported by bash.
+ var_pattern.append(r'\d.*')
+ var_pattern.append(r'.*\W.*')
+
+ var_pattern = "^(%s)$" % "|".join(var_pattern)
filter_bash_environment(
- compile_egrep_pattern(args[0]), file_in, file_out)
+ re.compile(var_pattern), file_in, file_out)
file_out.flush()