1#! /usr/bin/env python3
2
3r"""
4SVN helper script.
5
6Try to set the svn:eol-style property to "native" on every .py, .txt, .c and
7.h file in the directory tree rooted at the current directory.
8
9Files with the svn:eol-style property already set (to anything) are skipped.
10
11svn will itself refuse to set this property on a file that's not under SVN
12control, or that has a binary mime-type property set.  This script inherits
13that behavior, and passes on whatever warning message the failing "svn
14propset" command produces.
15
16In the Python project, it's safe to invoke this script from the root of
17a checkout.
18
19No output is produced for files that are ignored.  For a file that gets
20svn:eol-style set, output looks like:
21
22    property 'svn:eol-style' set on 'Lib\ctypes\__init__.py'
23
24For a file not under version control:
25
26    svn: warning: 'patch-finalizer.txt' is not under version control
27
28and for a file with a binary mime-type property:
29
30    svn: File 'Lib\test\test_pep263.py' has binary mime type property
31"""
32
33import re
34import os
35import sys
36import subprocess
37
38
39def propfiles(root, fn):
40    default = os.path.join(root, ".svn", "props", fn + ".svn-work")
41    try:
42        format = int(open(os.path.join(root, ".svn", "format")).read().strip())
43    except IOError:
44        return []
45    if format in (8, 9):
46        # In version 8 and 9, committed props are stored in prop-base, local
47        # modifications in props
48        return [os.path.join(root, ".svn", "prop-base", fn + ".svn-base"),
49                os.path.join(root, ".svn", "props", fn + ".svn-work")]
50    raise ValueError("Unknown repository format")
51
52
53def proplist(root, fn):
54    """Return a list of property names for file fn in directory root."""
55    result = []
56    for path in propfiles(root, fn):
57        try:
58            f = open(path)
59        except IOError:
60            # no properties file: not under version control,
61            # or no properties set
62            continue
63        while True:
64            # key-value pairs, of the form
65            # K <length>
66            # <keyname>NL
67            # V length
68            # <value>NL
69            # END
70            line = f.readline()
71            if line.startswith("END"):
72                break
73            assert line.startswith("K ")
74            L = int(line.split()[1])
75            key = f.read(L)
76            result.append(key)
77            f.readline()
78            line = f.readline()
79            assert line.startswith("V ")
80            L = int(line.split()[1])
81            value = f.read(L)
82            f.readline()
83        f.close()
84    return result
85
86
87def set_eol_native(path):
88    cmd = 'svn propset svn:eol-style native "{}"'.format(path)
89    propset = subprocess.Popen(cmd, shell=True)
90    propset.wait()
91
92
93possible_text_file = re.compile(r"\.([hc]|py|txt|sln|vcproj)$").search
94
95
96def main():
97    for arg in sys.argv[1:] or [os.curdir]:
98        if os.path.isfile(arg):
99            root, fn = os.path.split(arg)
100            if 'svn:eol-style' not in proplist(root, fn):
101                set_eol_native(arg)
102        elif os.path.isdir(arg):
103            for root, dirs, files in os.walk(arg):
104                if '.svn' in dirs:
105                    dirs.remove('.svn')
106                for fn in files:
107                    if possible_text_file(fn):
108                        if 'svn:eol-style' not in proplist(root, fn):
109                            path = os.path.join(root, fn)
110                            set_eol_native(path)
111
112
113if __name__ == '__main__':
114    main()
115