changeset 2378:a7f4b02f5fdb

fix the quoting mess * double double-quotes (") to get a double quote inside quotes * quote things correctly when needed (not just "%s") * add and fix unit tests for this quoting * formatter must quote the name before it can pass it to resolve_wiki
author Johannes Berg <johannes AT sipsolutions DOT net>
date Sun, 15 Jul 2007 16:34:25 +0200
parents 656685f94105
children 24bfe5789665 0c56b1468e78 d915ef70ebc9
files MoinMoin/_tests/test_wikiutil.py MoinMoin/formatter/text_html.py MoinMoin/parser/_tests/test_text_moin_wiki.py MoinMoin/parser/text_moin_wiki.py MoinMoin/wikiutil.py
diffstat 5 files changed, 52 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/test_wikiutil.py	Sun Jul 15 14:05:12 2007 +0200
+++ b/MoinMoin/_tests/test_wikiutil.py	Sun Jul 15 16:34:25 2007 +0200
@@ -66,10 +66,10 @@
 
 
 class TestNameQuoting:
-    tests = [(u"", u"''"), # empty
-             (u"test", u"'test'"), # nothing special
-             (u"Sarah O'Connor", u"\"Sarah O'Connor\""), # contains ', quote with "
-             (u'Just "something" quoted', u'\'Just "something" quoted\''), # contains ", quote with '
+    tests = [(u"", u'""'), # empty
+             (u"test", u'"test"'), # nothing special
+             (u"Sarah O'Connor", u"\"Sarah O'Connor\""),
+             (u'Just "something" quoted', u'"Just ""something"" quoted"'),
             ]
     def testQuoteName(self):
         for name, qname in self.tests:
@@ -85,7 +85,13 @@
         tests = [('SomePage', ('Self', 'SomePage', '')),
                  ('OtherWiki:OtherPage', ('OtherWiki', 'OtherPage', '')),
                  ('MoinMoin:"Page with blanks" link title', ("MoinMoin", "Page with blanks", "link title")),
-                 ("MoinMoin:'Page with blanks' link title", ("MoinMoin", "Page with blanks", "link title")),
+                 ('MoinMoin:"Page with blanks"link title', ("MoinMoin", "Page with blanks", "link title")),
+                 ('MoinMoin:"Page with blanks"', ("MoinMoin", "Page with blanks", "")),
+                 ('MoinMoin:"Page with ""quote""" link title', ("MoinMoin", 'Page with "quote"', "link title")),
+                 ('MoinMoin:"Page with """"double-quote"""link title', ("MoinMoin", 'Page with ""double-quote"', "link title")),
+                 ('MoinMoin:"""starts with quote"link title', ("MoinMoin", '"starts with quote', "link title")),
+                 ('MoinMoin:"ends with quote"""link title', ("MoinMoin", 'ends with quote"', "link title")),
+                 ('MoinMoin:"""page with quotes around"""link title', ("MoinMoin", '"page with quotes around"', "link title")),
                  ('attachment:"filename with blanks.txt" other title', ("attachment", "filename with blanks.txt", "other title")),
                 ]
         for markup, (wikiname, pagename, linktext) in tests:
--- a/MoinMoin/formatter/text_html.py	Sun Jul 15 14:05:12 2007 +0200
+++ b/MoinMoin/formatter/text_html.py	Sun Jul 15 16:34:25 2007 +0200
@@ -510,7 +510,8 @@
         """
         @keyword title: override using the interwiki wikiname as title
         """
-        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:"%s"' % (interwiki, pagename))
+        quoted = '%s:%s' % (interwiki, wikiutil.quoteName(pagename))
+        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, quoted)
         wikiurl = wikiutil.mapURL(self.request, wikiurl)
         if wikitag == 'Self': # for own wiki, do simple links
             if '#' in wikitail:
--- a/MoinMoin/parser/_tests/test_text_moin_wiki.py	Sun Jul 15 14:05:12 2007 +0200
+++ b/MoinMoin/parser/_tests/test_text_moin_wiki.py	Sun Jul 15 16:34:25 2007 +0200
@@ -586,11 +586,26 @@
         ('MoinMoin:"with space"',     '<a class="interwiki" href="http://moinmoin.wikiwikiweb.de/with%20space" title="MoinMoin">with space</a>'),
         ('RFC:"1 2 3"',               '<a class="interwiki" href="http://www.ietf.org/rfc/rfc1%202%203" title="RFC">1 2 3</a>'),
         ("RFC:'something else'",      "RFC:'something else'"),
+        ('["with "" quote"]',         '<a class="nonexistent" href="./with%20%22%20quote">with " quote</a>'),
+        ('MoinMoin:"with "" quote"',  '<a class="interwiki" href="http://moinmoin.wikiwikiweb.de/with%20%22%20quote" title="MoinMoin">with " quote</a>'),
         )
 
     def testTextFormating(self):
         """ parser.wiki: text formating """
+        together_test = []
+        together_expected = []
         for test, expected in self._tests:
             html = self.parse(self.text % test)
             result = self.needle.search(html).group(1)
             assert result == expected
+            together_test.append(test)
+            together_expected.append(expected)
+
+        # now test all together to make sure one quoting doesn't
+        # "leak" into the next
+        for joint in ('', 'lala " lala ', 'lala "" lala '):
+            test = joint.join(together_test)
+            expected = joint.join(together_expected)
+            html = self.parse(self.text % test)
+            result = self.needle.search(html).group(1)
+            assert result == expected
--- a/MoinMoin/parser/text_moin_wiki.py	Sun Jul 15 14:05:12 2007 +0200
+++ b/MoinMoin/parser/text_moin_wiki.py	Sun Jul 15 16:34:25 2007 +0200
@@ -33,9 +33,7 @@
     # quoted strings (we require that there is at least one char (that is not the quoting char)
     # inside to not confuse stuff like '''Contact:''' (just a bold Contact:) with interwiki markup
     # OtherWiki:'Page with blanks'
-    sq_string = ur"('[^']+?')" # single quoted string
-    dq_string = ur"(\"[^\"]+?\")" # double quoted string
-    q_string = ur"(%s|%s)" % (sq_string, dq_string) # quoted string
+    dq_string = ur'("([^"]|"")+?"(?!"))' # double quoted string
     attachment_schemas = ["attachment", "inline", "drawing"]
     punct_pattern = re.escape(u'''"\'}]|:,.)?!''')
     punct_no_quote_pattern = re.escape(u'''}]|:,.)?!''')
@@ -50,12 +48,12 @@
         'subpages': wikiutil.CHILD_PREFIX + '?',
         'parent': ur'(?:%s)?' % re.escape(PARENT_PREFIX),
     }
-    url_rule = ur'%(url_guard)s(%(url)s)\:(([^\s\<%(punct)s]|([%(punctnq)s][^\s\<%(punct)s]))+|%(q_string)s)' % {
+    url_rule = ur'%(url_guard)s(%(url)s)\:(([^\s\<%(punct)s]|([%(punctnq)s][^\s\<%(punct)s]))+|%(dq_string)s)' % {
         'url_guard': ur'(^|(?<!\w))',
         'url': url_pattern,
         'punct': punct_pattern,
         'punctnq': punct_no_quote_pattern,
-        'q_string': q_string,
+        'dq_string': dq_string,
     }
 
     ol_rule = ur"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
@@ -405,16 +403,10 @@
 
     def _wikiname_bracket_repl(self, text):
         """Handle special-char wikinames with link text, like:
-           ["Jim O'Brian" Jim's home page] or ['Hello "world"!' a page with doublequotes]i
+           ["Jim O'Brian" Jim's home page] or ["Hello ""world""!" a page with doublequotes]
         """
         word = text[1:-1] # strip brackets
-        first_char = word[0]
-        if first_char in wikiutil.QUOTE_CHARS:
-            # split on closing quote
-            target, linktext = word[1:].split(first_char, 1)
-        else: # not quoted
-            # split on whitespace
-            target, linktext = word.split(None, 1)
+        empty, target, linktext = wikiutil.split_wiki(':%s' % word)
         if target:
             linktext = linktext.strip()
             return self._word_repl(target, linktext)
--- a/MoinMoin/wikiutil.py	Sun Jul 15 14:05:12 2007 +0200
+++ b/MoinMoin/wikiutil.py	Sun Jul 15 16:34:25 2007 +0200
@@ -482,23 +482,19 @@
 
 # Quoting of wiki names, file names, etc. (in the wiki markup) -----------------------------------
 
+# don't ever change this
 QUOTE_CHARS = u'"'
 
 def quoteName(name):
     """ put quotes around a given name """
-    for quote_char in QUOTE_CHARS:
-        if quote_char not in name:
-            return u"%s%s%s" % (quote_char, name, quote_char)
-    else:
-        return name # XXX we need to be able to escape the quote char for worst case
+    return '"%s"' % name.replace('"', '""')
 
 def unquoteName(name):
     """ if there are quotes around the name, strip them """
     if not name:
         return name
-    for quote_char in QUOTE_CHARS:
-        if quote_char == name[0] == name[-1]:
-            return name[1:-1]
+    if '"' == name[0] == name[-1]:
+        return name[1:-1].replace('""', '"')
     else:
         return name
 
@@ -610,9 +606,21 @@
         except ValueError:
             wikiname, rest = 'Self', wikiurl
     if rest:
-        first_char = rest[0]
-        if first_char in QUOTE_CHARS: # quoted pagename
-            pagename_linktext = rest[1:].split(first_char, 1)
+        if rest[0] == '"': # quoted pagename
+            idx = 1
+            max = len(rest)
+            while idx < max:
+                if idx + 1 < max:
+                    next = rest[idx + 1]
+                else:
+                    next = None
+                if next == rest[idx] == '"':
+                    idx += 2
+                    continue
+                if next != '"' and rest[idx] == '"':
+                    break
+                idx += 1
+            pagename_linktext = rest[1:idx].replace('""', '"'), rest[idx+1:]
         else: # not quoted, split on whitespace
             pagename_linktext = rest.split(None, 1)
     else: