from distutils.command.sdist import sdist as _sdist
from distutils.util import convert_path
import os,re

entities = [
    ("&lt;","<"), ("&gt;", ">"), ("&quot;", '"'), ("&apos;", "'"),
    ("&amp;", "&")
]
def unescape(data):
    for old,new in entities:
        data = data.replace(old,new)
    return data

patterns = [
    (convert_path('CVS/Entries'), re.compile(r"^\w?/([^/]+)/", re.M), None),
    (convert_path('.svn/entries'), re.compile(r'name="([^"]+)"'), unescape),
]

def joinpath(prefix,suffix):
    if not prefix:
        return suffix
    return os.path.join(prefix,suffix)



















def walk_revctrl(dirname='', memo=None):
    """Find all files under revision control"""
    if memo is None:
        memo = {}
    if dirname in memo:
        # Don't rescan a scanned directory
        return
    for path, pattern, postproc in patterns:
        path = joinpath(dirname,path)
        if os.path.isfile(path):
            f = open(path,'rU')
            data = f.read()
            f.close()
            for match in pattern.finditer(data):
                path = match.group(1)
                if postproc:
                    path = postproc(path)
                path = joinpath(dirname,path)
                if os.path.isfile(path):
                    yield path
                elif os.path.isdir(path):
                    for item in walk_revctrl(path, memo):
                        yield item


















class sdist(_sdist):
    """Smart sdist that finds anything supported by revision control"""

    def run(self):
        self.run_command('egg_info')
        _sdist.run(self)
        dist_files = getattr(self.distribution,'dist_files',[])
        for file in self.archive_files:
            data = ('sdist', '', file)
            if data not in dist_files:
                dist_files.append(data)

    def finalize_options(self):
        _sdist.finalize_options(self)
        if not os.path.isfile(self.template):
            self.force_manifest = 1     # always regen if no template

    def add_defaults(self):
        _sdist.add_defaults(self)
        self.filelist.extend(walk_revctrl())






















@@ -200,16 +200,17 @@
  %22&%22)%0A%5D%0A
+%0A
 def unes
@@ -308,176 +308,352 @@
 ta%0A%0A
-patterns = %5B%0A    (convert_path('CVS/Entries'), re.compile(r%22%5E%5Cw?/(%5B%5E/%5D+)/%22, re.M), None),%0A    (convert_path('.svn/entries'), re.compile(r'name=%22(%5B%5E%22%5D+)%22'), unescape),%0A%5D
+def re_finder(pattern, postproc=None):%0A    def find(dirname, filename):%0A        f = open(filename,'rU')%0A        data = f.read()%0A        f.close()%0A        for match in pattern.finditer(data):%0A            path = match.group(1)%0A            if postproc:%0A                path = postproc(path)%0A            yield joinpath(dirname,path)%0A    return find
 %0A%0Ade
@@ -770,24 +770,16 @@
 %0A%0A%0A%0A%0A%0A%0A%0A
-%0A%0A%0A%0A%0A%0A%0A%0A
 def walk
@@ -998,36 +998,24 @@
 th, 
-pattern, postproc in patt
+finder in find
 er
-n
 s:%0A 
@@ -1101,123 +1101,152 @@
    f
- = open(path,'rU')%0A            data = f.read()%0A            f.close()%0A            for match in pattern.finditer(data
+or path in finder(dirname,path):%0A                if os.path.isfile(path):%0A                    yield path%0A                elif os.path.isdir(path
 ):%0A 
@@ -1264,369 +1264,1008 @@
     
-path = match.group(1)%0A                if postproc:%0A                    path = postproc(path)%0A                path = joinpath(dirname,path)%0A                if os.path.isfile(path):%0A                    yield path%0A                elif os.path.isdir(path):%0A                    for item in walk_revctrl(path, memo):%0A                        yield item%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A
+    for item in walk_revctrl(path, memo):%0A                        yield item%0A%0Adef externals_finder(dirname, filename):%0A    %22%22%22Find any 'svn:externals' directories%22%22%22%0A    found = False%0A    f = open(filename,'rb')%0A    for line in iter(f.readline, ''):    # can't use direct iter!%0A        parts = line.split()%0A        if len(parts)==2:%0A            kind,length = parts%0A            data = f.read(int(length))%0A            if kind=='K' and data=='svn:externals':%0A                found = True%0A            elif kind=='V' and found:%0A                f.close()%0A                break%0A    else:%0A        f.close()%0A        return%0A%0A    for line in data.splitlines():%0A        parts = line.split()%0A        if parts:%0A            yield joinpath(dirname, parts%5B0%5D)%0A%0A%0Afinders = %5B%0A    (convert_path('CVS/Entries'),%0A        re_finder(re.compile(r%22%5E%5Cw?/(%5B%5E/%5D+)/%22, re.M))),%0A    (convert_path('.svn/entries'),%0A        re_finder(re.compile(r'name=%22(%5B%5E%22%5D+)%22'), unescape)),%0A    (convert_path('.svn/dir-props'), externals_finder),%0A%5D
 %0A%0A%0Ac
@@ -2955,29 +2955,20 @@
 ctrl())%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A%0A
-%0A%0A%0A%0A%0A%0A%0A%0A%0A

Include ``svn:externals`` directories in source distributions as well as normal subversion-controlled files and directories.