16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
from shutil import copyfile
20
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
21
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
22
19
from cStringIO import StringIO
22
from os import listdir
26
from shutil import copyfile
28
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
29
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
36
from ntpath import (abspath as _nt_abspath,
38
normpath as _nt_normpath,
39
realpath as _nt_realpath,
35
43
from bzrlib.errors import (BzrError,
78
mode = os.lstat(f)[ST_MODE]
86
_directory_kind = 'directory'
89
stat.S_IFDIR:_directory_kind,
90
stat.S_IFCHR:'chardev',
94
stat.S_IFLNK:'symlink',
95
stat.S_IFSOCK:'socket',
99
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
100
"""Generate a file kind from a stat mode. This is used in walkdirs.
102
Its performance is critical: Do not mutate without careful benchmarking.
105
return _formats[stat_mode & 0170000]
110
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
112
return _mapper(_lstat(f).st_mode)
114
if getattr(e, 'errno', None) == errno.ENOENT:
115
raise bzrlib.errors.NoSuchFile(f)
97
119
def kind_marker(kind):
98
120
if kind == 'file':
100
elif kind == 'directory':
122
elif kind == _directory_kind:
102
124
elif kind == 'symlink':
105
127
raise BzrError('invalid file kind %r' % kind)
108
if hasattr(os.path, 'lexists'):
109
return os.path.lexists(f)
111
if hasattr(os, 'lstat'):
117
if e.errno == errno.ENOENT:
120
raise BzrError("lstat/stat of (%r): %r" % (f, e))
129
lexists = getattr(os.path, 'lexists', None)
133
if hasattr(os, 'lstat'):
139
if e.errno == errno.ENOENT:
142
raise BzrError("lstat/stat of (%r): %r" % (f, e))
122
145
def fancy_rename(old, new, rename_func, unlink_func):
123
146
"""A fancy rename, when you don't have atomic rename.
174
197
rename_func(tmp_name, new)
200
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
201
# choke on a Unicode string containing a relative path if
202
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
204
_fs_enc = sys.getfilesystemencoding()
205
def _posix_abspath(path):
206
return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
207
# jam 20060426 This is another possibility which mimics
208
# os.path.abspath, only uses unicode characters instead
209
# if not os.path.isabs(path):
210
# return os.path.join(os.getcwdu(), path)
214
def _posix_realpath(path):
215
return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
218
def _win32_abspath(path):
219
return _nt_abspath(path.encode(_fs_enc)).decode(_fs_enc).replace('\\', '/')
222
def _win32_realpath(path):
223
return _nt_realpath(path.encode(_fs_enc)).decode(_fs_enc).replace('\\', '/')
226
def _win32_pathjoin(*args):
227
return _nt_join(*args).replace('\\', '/')
230
def _win32_normpath(path):
231
return _nt_normpath(path).replace('\\', '/')
235
return os.getcwdu().replace('\\', '/')
238
def _win32_mkdtemp(*args, **kwargs):
239
return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
242
def _win32_rename(old, new):
243
fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
176
246
# Default is to just use the python builtins, but these can be rebound on
177
247
# particular platforms.
178
abspath = os.path.abspath
179
realpath = os.path.realpath
248
abspath = _posix_abspath
249
realpath = _posix_realpath
180
250
pathjoin = os.path.join
181
251
normpath = os.path.normpath
182
252
getcwd = os.getcwdu
189
259
MIN_ABS_PATHLENGTH = 1
191
if os.name == "posix":
192
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
193
# choke on a Unicode string containing a relative path if
194
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
196
_fs_enc = sys.getfilesystemencoding()
198
return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
201
return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
203
262
if sys.platform == 'win32':
204
# We need to use the Unicode-aware os.path.abspath and
205
# os.path.realpath on Windows systems.
207
return os.path.abspath(path).replace('\\', '/')
210
return os.path.realpath(path).replace('\\', '/')
213
return os.path.join(*args).replace('\\', '/')
216
return os.path.normpath(path).replace('\\', '/')
219
return os.getcwdu().replace('\\', '/')
221
def mkdtemp(*args, **kwargs):
222
return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
224
def rename(old, new):
225
fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
263
abspath = _win32_abspath
264
realpath = _win32_realpath
265
pathjoin = _win32_pathjoin
266
normpath = _win32_normpath
267
getcwd = _win32_getcwd
268
mkdtemp = _win32_mkdtemp
269
rename = _win32_rename
227
271
MIN_ABS_PATHLENGTH = 3
399
def is_inside_or_parent_of_any(dir_list, fname):
400
"""True if fname is a child or a parent of any of the given files."""
401
for dirname in dir_list:
402
if is_inside(dirname, fname) or is_inside(fname, dirname):
355
408
def pumpfile(fromfile, tofile):
356
409
"""Copy contents of one file to another."""
666
719
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
722
_platform_normalizes_filenames = False
723
if sys.platform == 'darwin':
724
_platform_normalizes_filenames = True
727
def normalizes_filenames():
728
"""Return True if this platform normalizes unicode filenames.
730
Mac OSX does, Windows/Linux do not.
732
return _platform_normalizes_filenames
735
if _platform_normalizes_filenames:
736
def unicode_filename(path):
737
"""Make sure 'path' is a properly normalized filename.
739
On platforms where the system normalizes filenames (Mac OSX),
740
you can access a file by any path which will normalize
742
Internally, bzr only supports NFC/NFKC normalization, since
743
that is the standard for XML documents.
744
So we return an normalized path, and indicate this has been
747
:return: (path, is_normalized) Return a path which can
748
access the file, and whether or not this path is
751
return unicodedata.normalize('NFKC', path), True
753
def unicode_filename(path):
754
"""Make sure 'path' is a properly normalized filename.
756
On platforms where the system does not normalize filenames
757
(Windows, Linux), you have to access a file by its exact path.
758
Internally, bzr only supports NFC/NFKC normalization, since
759
that is the standard for XML documents.
760
So we return the original path, and indicate if this is
763
:return: (path, is_normalized) Return a path which can
764
access the file, and whether or not this path is
767
return path, unicodedata.normalize('NFKC', path) == path
669
770
def terminal_width():
670
771
"""Return estimated terminal width."""
671
772
if sys.platform == 'win32':
693
794
return sys.platform != "win32"
696
def strip_trailing_slash(path):
697
"""Strip trailing slash, except for root paths.
698
The definition of 'root path' is platform-dependent.
700
if len(path) != MIN_ABS_PATHLENGTH and path[-1] == '/':
706
797
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
716
807
if _validWin32PathRE.match(path) is None:
717
808
raise IllegalPath(path)
811
def walkdirs(top, prefix=""):
812
"""Yield data about all the directories in a tree.
814
This yields all the data about the contents of a directory at a time.
815
After each directory has been yielded, if the caller has mutated the list
816
to exclude some directories, they are then not descended into.
818
The data yielded is of the form:
819
[(relpath, basename, kind, lstat, path_from_top), ...]
821
:param prefix: Prefix the relpaths that are yielded with 'prefix'. This
822
allows one to walk a subtree but get paths that are relative to a tree
824
:return: an iterator over the dirs.
828
_directory = _directory_kind
830
pending = [(prefix, "", _directory, None, top)]
833
currentdir = pending.pop()
834
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
837
relroot = currentdir[0] + '/'
840
for name in sorted(_listdir(top)):
841
abspath = top + '/' + name
842
statvalue = lstat(abspath)
843
dirblock.append ((relroot + name, name, file_kind_from_stat_mode(statvalue.st_mode), statvalue, abspath))
845
# push the user specified dirs from dirblock
846
for dir in reversed(dirblock):
847
if dir[2] == _directory: