Sync the fixes for bug #211949 from trunk.
authorZac Medico <zmedico@gentoo.org>
Fri, 14 Mar 2008 18:20:49 +0000 (18:20 -0000)
committerZac Medico <zmedico@gentoo.org>
Fri, 14 Mar 2008 18:20:49 +0000 (18:20 -0000)
svn path=/main/branches/2.1.2/; revision=9465

bin/ebuild.sh
bin/filter-bash-environment.py

index 2f17d0e7e6765aac860b0f0b0462468037e3f844..3cf834f6be3f9c76ad029991d7535c443e72c824 100755 (executable)
@@ -1412,18 +1412,16 @@ PORTAGE_MUTABLE_FILTERED_VARS="AA HOSTNAME"
 # 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
@@ -1437,20 +1435,8 @@ filter_readonly_variables() {
                        ${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
index 691d406da7f06e8f32e4e948baaafd0e63ffcfa2..d3434ff0e18d25827324e5d91c0b17c34ccf35b3 100755 (executable)
@@ -5,22 +5,62 @@
 
 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
@@ -31,15 +71,29 @@ def filter_bash_environment(pattern, file_in, file_out):
                        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)
@@ -48,6 +102,13 @@ if __name__ == "__main__":
                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()