Bug 899875 - Better handle empty arguments in pymake, and also treat whitespaces in bulk. r=ted
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 02 Aug 2013 10:29:31 +0900
changeset 346 1cf15f6116e8
parent 345 4502b185d064
child 347 cd96f5cee068
push id219
push usermh@glandium.org
push dateFri, 02 Aug 2013 01:34:28 +0000
reviewersted
bugs899875
Bug 899875 - Better handle empty arguments in pymake, and also treat whitespaces in bulk. r=ted
pymake/process.py
tests/empty-arg.mk
--- a/pymake/process.py
+++ b/pymake/process.py
@@ -28,17 +28,17 @@ def tokens2re(tokens):
     # The group names and patterns come are given as a dict in the function
     # argument.
     nonescaped = r'(?<!\\)(?:%s)' % '|'.join('(?P<%s>%s)' % (name, value) for name, value in tokens.iteritems())
     # The final pattern matches either the above pattern, or an escaped
     # backslash, captured in the "escape" match group.
     return re.compile('(?:%s|%s)' % (nonescaped, r'(?P<escape>\\\\)'))
 
 _unquoted_tokens = tokens2re({
-  'whitespace': r'[\t\r\n ]',
+  'whitespace': r'[\t\r\n ]+',
   'quote': r'[\'"]',
   'comment': '#',
   'special': r'[<>&|`~(){}$;]',
   'backslashed': r'\\[^\\]',
   'glob': r'[\*\?]',
 })
 
 _doubly_quoted_tokens = tokens2re({
@@ -54,66 +54,69 @@ class MetaCharacterException(Exception):
 
 class ClineSplitter(list):
     """
     Parses a given command line string and creates a list of command
     and arguments, with wildcard expansion.
     """
     def __init__(self, cline, cwd):
         self.cwd = cwd
-        self.arg = ''
+        self.arg = None
         self.cline = cline
         self.glob = False
         self._parse_unquoted()
 
     def _push(self, str):
         """
         Push the given string as part of the current argument
         """
+        if self.arg is None:
+            self.arg = ''
         self.arg += str
 
     def _next(self):
         """
         Finalize current argument, effectively adding it to the list.
         Perform globbing if needed.
         """
-        if not self.arg:
+        if self.arg is None:
             return
         if self.glob:
             if os.path.isabs(self.arg):
                 path = self.arg
             else:
                 path = os.path.join(self.cwd, self.arg)
             globbed = glob.glob(path)
             if not globbed:
                 # If globbing doesn't find anything, the literal string is
                 # used.
                 self.append(self.arg)
             else:
                 self.extend(f[len(path)-len(self.arg):] for f in globbed)
             self.glob = False
         else:
             self.append(self.arg)
-        self.arg = ''
+        self.arg = None
 
     def _parse_unquoted(self):
         """
         Parse command line remainder in the context of an unquoted string.
         """
         while self.cline:
             # Find the next token
             m = _unquoted_tokens.search(self.cline)
             # If we find none, the remainder of the string can be pushed to
             # the current argument and the argument finalized
             if not m:
                 self._push(self.cline)
                 break
             # The beginning of the string, up to the found token, is part of
             # the current argument
-            self._push(self.cline[:m.start()])
+            if m.start():
+                self._push(self.cline[:m.start()])
             self.cline = self.cline[m.end():]
 
             match = dict([(name, value) for name, value in m.groupdict().items() if value])
             if 'quote' in match:
                 # " or ' start a quoted string
                 if match['quote'] == '"':
                     self._parse_doubly_quoted()
                 else:
@@ -137,17 +140,18 @@ class ClineSplitter(list):
                 # e.g. echo \a -> a
                 self._push(match['backslashed'][1])
             elif 'glob' in match:
                 # ? or * will need globbing
                 self.glob = True
                 self._push(m.group(0))
             else:
                 raise Exception, "Shouldn't reach here"
-        self._next()
+        if self.arg:
+            self._next()
 
     def _parse_quoted(self):
         # Single quoted strings are preserved, except for the final quote
         index = self.cline.find("'")
         if index == -1:
             raise Exception, 'Unterminated quoted string in command'
         self._push(self.cline[:index])
         self.cline = self.cline[index+1:]
new file mode 100644
--- /dev/null
+++ b/tests/empty-arg.mk
@@ -0,0 +1,2 @@
+all:
+	@  sh -c 'if [ $$# = 3 ] ; then echo TEST-PASS; else echo TEST-FAIL; fi' -- a "" b