# HG changeset patch
# Parent df11bf9aa02205ac82e85bc610cb835f846becd7
CamelCase: the support for unicode (#230) could lead to some pathological run-time, in presence of unicode words that didn't contain any alphanumerical characters.
Changed the approach to use a proper list of unicode upper and lower characters.
Fixes #9025.
diff --git a/trac/wiki/api.py b/trac/wiki/api.py
|
a
|
b
|
class WikiSystem(Component): |
| 230 | 230 | # here adapted to exclude terminal "." and ":" characters |
| 231 | 231 | |
| 232 | 232 | PAGE_SPLIT_RE = re.compile(r"([a-z])([A-Z])(?=[a-z])") |
| 233 | | |
| | 233 | |
| | 234 | Lu = ''.join(unichr(c) for c in range(0, 0x10000) if unichr(c).isupper()) |
| | 235 | Ll = ''.join(unichr(c) for c in range(0, 0x10000) if unichr(c).islower()) |
| | 236 | |
| 234 | 237 | def format_page_name(self, page, split=False): |
| 235 | 238 | if split or self.split_page_names: |
| 236 | 239 | return self.PAGE_SPLIT_RE.sub(r"\1 \2", page) |
| 237 | 240 | return page |
| 238 | 241 | |
| 239 | 242 | def get_wiki_syntax(self): |
| 240 | | lower = r'(?<![A-Z0-9_])' # No Upper case when looking behind |
| 241 | | upper = r'(?<![a-z0-9_])' # No Lower case when looking behind |
| 242 | 243 | wiki_page_name = ( |
| 243 | | r"\w%s(?:\w%s)+(?:\w%s(?:\w%s)*[\w/]%s)+" % # wiki words |
| 244 | | (upper, lower, upper, lower, lower) + |
| | 244 | r"[%(upper)s](?:[%(lower)s])+(?:[%(upper)s](?:[%(lower)s])+/?)+" |
| | 245 | # wiki words |
| 245 | 246 | r"(?:@\d+)?" # optional version |
| 246 | | r"(?:#%s)?" % self.XML_NAME + # optional fragment id |
| 247 | | r"(?=:(?:\Z|\s)|[^:a-zA-Z]|\s|\Z)" # what should follow it |
| 248 | | ) |
| 249 | | |
| | 247 | r"(?:#%(xml)s)?" # optional fragment id |
| | 248 | r"(?=:(?:\Z|\s)|[^:%(upper)s%(lower)s]|\s|\Z)" |
| | 249 | # what should follow it |
| | 250 | % {'upper': self.Lu, 'lower': self.Ll, |
| | 251 | # % {'upper': _re_ranges(self.Lu), 'lower': _re_ranges(self.Ll), |
| | 252 | 'xml': self.XML_NAME}) |
| 250 | 253 | |
| 251 | 254 | # Regular WikiPageNames |
| 252 | 255 | def wikipagename_link(formatter, match, fullmatch): |
| 253 | | if not _check_unicode_camelcase(match): |
| 254 | | return match |
| 255 | 256 | return self._format_link(formatter, 'wiki', match, |
| 256 | 257 | self.format_page_name(match), |
| 257 | 258 | self.ignore_missing_pages, match) |
| … |
… |
class WikiSystem(Component): |
| 263 | 264 | def wikipagename_with_label_link(formatter, match, fullmatch): |
| 264 | 265 | page = fullmatch.group('wiki_page') |
| 265 | 266 | label = fullmatch.group('wiki_label') |
| 266 | | if not _check_unicode_camelcase(page): |
| 267 | | return label |
| 268 | 267 | return self._format_link(formatter, 'wiki', page, label.strip(), |
| 269 | 268 | self.ignore_missing_pages, match) |
| 270 | 269 | yield (r"!?\[(?P<wiki_page>%s)\s+(?P<wiki_label>%s|[^\]]+)\]" |
| … |
… |
class WikiSystem(Component): |
| 382 | 381 | return self.format_page_name(resource.id) |
| 383 | 382 | |
| 384 | 383 | |
| 385 | | def _check_unicode_camelcase(pagename): |
| 386 | | """A camelcase word must have at least 2 humps (well...) |
| | 384 | def _re_ranges(chars): |
| | 385 | r = l = chars[0] |
| | 386 | ranges = [] |
| | 387 | for c in chars[1:]: |
| | 388 | if ord(c) - ord(r) == 1: |
| | 389 | r = c |
| | 390 | elif ord(r) - ord(l) > 1: |
| | 391 | ranges.append('%s-%s' % (l, r)) |
| | 392 | r = l = c |
| | 393 | else: |
| | 394 | ranges.append(r) |
| | 395 | r = l = c |
| | 396 | return ''.join(ranges) |
| 387 | 397 | |
| 388 | | >>> _check_unicode_camelcase(u"\xc9l\xe9phant") |
| 389 | | False |
| 390 | | >>> _check_unicode_camelcase(u"\xc9l\xe9Phant") |
| 391 | | True |
| 392 | | >>> _check_unicode_camelcase(u"\xe9l\xe9Phant") |
| 393 | | False |
| 394 | | >>> _check_unicode_camelcase(u"\xc9l\xe9PhanT") |
| 395 | | False |
| 396 | | """ |
| 397 | | if not pagename[0].isupper(): |
| 398 | | return False |
| 399 | | pagename = pagename.split('@', 1)[0].split('#', 1)[0] |
| 400 | | if not pagename[-1].islower(): |
| 401 | | return False |
| 402 | | humps = 0 |
| 403 | | for i in xrange(1, len(pagename)): |
| 404 | | if pagename[i-1].isupper(): |
| 405 | | if pagename[i].islower(): |
| 406 | | humps += 1 |
| 407 | | else: |
| 408 | | return False |
| 409 | | return humps > 1 |
| 410 | | |