1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2006 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module contains classes to set Subversion properties on files."""
27 from common
import warning_prefix
31 class SVNPropertySetter
:
32 """Abstract class for objects that can set properties on a SVNCommitItem."""
34 def set_properties(self
, s_item
):
35 """Set any properties that can be determined for S_ITEM."""
37 raise NotImplementedError
40 class CVSRevisionNumberSetter(SVNPropertySetter
):
41 """Set the cvs2svn:cvs-rev property to the CVS revision number."""
43 def set_properties(self
, s_item
):
44 s_item
.svn_props
['cvs2svn:cvs-rev'] = s_item
.c_rev
.rev
45 s_item
.svn_props_changed
= True
48 class ExecutablePropertySetter(SVNPropertySetter
):
49 """Set the svn:executable property based on c_rev.cvs_file.executable."""
51 def set_properties(self
, s_item
):
52 if s_item
.c_rev
.cvs_file
.executable
:
53 s_item
.svn_props
['svn:executable'] = '*'
56 class BinaryFileEOLStyleSetter(SVNPropertySetter
):
57 """Set the eol-style for binary files to None."""
59 def set_properties(self
, s_item
):
60 if s_item
.c_rev
.cvs_file
.mode
== 'b':
61 s_item
.svn_props
['svn:eol-style'] = None
64 class MimeMapper(SVNPropertySetter
):
65 """A class that provides mappings from file names to MIME types."""
67 def __init__(self
, mime_types_file
):
70 for line
in fileinput
.input(mime_types_file
):
71 if line
.startswith("#"):
74 # format of a line is something like
76 extensions
= line
.split()
77 if len(extensions
) < 2:
79 type = extensions
.pop(0)
80 for ext
in extensions
:
81 if self
.mappings
.has_key(ext
) and self
.mappings
[ext
] != type:
82 sys
.stderr
.write("%s: ambiguous MIME mapping for *.%s (%s or %s)\n"
83 % (warning_prefix
, ext
, self
.mappings
[ext
], type))
84 self
.mappings
[ext
] = type
86 def set_properties(self
, s_item
):
87 basename
, extension
= os
.path
.splitext(
88 os
.path
.basename(s_item
.c_rev
.cvs_path
)
91 # Extension includes the dot, so strip it (will leave extension
92 # empty if filename ends with a dot, which is ok):
93 extension
= extension
[1:]
95 # If there is no extension (or the file ends with a period), use
96 # the base name for mapping. This allows us to set mappings for
97 # files such as README or Makefile:
101 mime_type
= self
.mappings
.get(extension
, None)
102 if mime_type
is not None:
103 s_item
.svn_props
['svn:mime-type'] = mime_type
106 class AutoPropsPropertySetter(SVNPropertySetter
):
107 """Set arbitrary svn properties based on an auto-props configuration.
109 This class supports case-sensitive or case-insensitive pattern
110 matching. The 'correct' behavior is not quite clear, because
111 subversion itself does an inconsistent job of handling case in
112 auto-props patterns; see
113 http://subversion.tigris.org/issues/show_bug.cgi?id=2036.
115 If a property specified in auto-props has already been set to a
116 different value, print a warning and leave the old property value
120 """Describes the properties to be set for files matching a pattern."""
122 def __init__(self
, pattern
, propdict
):
123 # A glob-like pattern:
124 self
.pattern
= pattern
125 # A dictionary of properties that should be set:
126 self
.propdict
= propdict
128 def match(self
, basename
):
129 """Does the file with the specified basename match pattern?"""
131 return fnmatch
.fnmatch(basename
, self
.pattern
)
133 def __init__(self
, configfilename
, ignore_case
):
134 config
= ConfigParser
.ConfigParser()
136 self
.transform_case
= self
.squash_case
138 config
.optionxform
= self
.preserve_case
139 self
.transform_case
= self
.preserve_case
141 config
.readfp(file(configfilename
))
143 for section
in config
.sections():
144 if self
.transform_case(section
) == 'auto-props':
145 for pattern
in config
.options(section
):
146 value
= config
.get(section
, pattern
)
148 self
._add
_pattern
(pattern
, value
)
150 def squash_case(self
, s
):
153 def preserve_case(self
, s
):
156 def _add_pattern(self
, pattern
, value
):
157 props
= value
.split(';')
160 s
= prop
.split('=', 1)
162 propdict
[s
[0]] = None
164 propdict
[s
[0]] = s
[1]
165 self
.patterns
.append(
166 self
.Pattern(self
.transform_case(pattern
), propdict
))
168 def get_propdict(self
, path
):
169 basename
= self
.transform_case(os
.path
.basename(path
))
171 for pattern
in self
.patterns
:
172 if pattern
.match(basename
):
173 for (key
,value
) in pattern
.propdict
.items():
174 if propdict
.has_key(key
):
175 if propdict
[key
] != value
:
177 "Contradictory values set for property '%s' for file %s."
180 propdict
[key
] = value
184 def set_properties(self
, s_item
):
185 propdict
= self
.get_propdict(s_item
.c_rev
.cvs_path
)
186 for (k
,v
) in propdict
.items():
187 if s_item
.svn_props
.has_key(k
):
188 if s_item
.svn_props
[k
] != v
:
190 "Property '%s' already set to %r for file %s; "
191 "auto-props value (%r) ignored."
192 % (k
, s_item
.svn_props
[k
], s_item
.c_rev
.cvs_path
, v
,))
194 s_item
.svn_props
[k
] = v
197 class BinaryFileDefaultMimeTypeSetter(SVNPropertySetter
):
198 """If the file is binary and its svn:mime-type property is not yet
199 set, set it to 'application/octet-stream'."""
201 def set_properties(self
, s_item
):
202 if not s_item
.svn_props
.has_key('svn:mime-type') \
203 and s_item
.c_rev
.cvs_file
.mode
== 'b':
204 s_item
.svn_props
['svn:mime-type'] = 'application/octet-stream'
207 class EOLStyleFromMimeTypeSetter(SVNPropertySetter
):
208 """Set svn:eol-style based on svn:mime-type.
210 If svn:mime-type is known but svn:eol-style is not, then set
211 svn:eol-style based on svn:mime-type as follows: if svn:mime-type
212 starts with 'text/', then set svn:eol-style to native; otherwise,
213 force it to remain unset. See also issue #39."""
215 def set_properties(self
, s_item
):
216 if not s_item
.svn_props
.has_key('svn:eol-style') \
217 and s_item
.svn_props
.get('svn:mime-type', None) is not None:
218 if s_item
.svn_props
['svn:mime-type'].startswith("text/"):
219 s_item
.svn_props
['svn:eol-style'] = 'native'
221 s_item
.svn_props
['svn:eol-style'] = None
224 class DefaultEOLStyleSetter(SVNPropertySetter
):
225 """Set the eol-style if one has not already been set."""
227 def __init__(self
, value
):
228 """Initialize with the specified default VALUE."""
232 def set_properties(self
, s_item
):
233 if not s_item
.svn_props
.has_key('svn:eol-style'):
234 s_item
.svn_props
['svn:eol-style'] = self
.value
237 class KeywordsPropertySetter(SVNPropertySetter
):
238 """If the svn:keywords property is not yet set, set it based on the
239 file's mode. See issue #2."""
241 def __init__(self
, value
):
242 """Use VALUE for the value of the svn:keywords property if it is
247 def set_properties(self
, s_item
):
248 if not s_item
.svn_props
.has_key('svn:keywords') \
249 and s_item
.c_rev
.cvs_file
.mode
in [None, 'kv', 'kvl']:
250 s_item
.svn_props
['svn:keywords'] = self
.value