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)
19
22
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,
43
34
from bzrlib.errors import (BzrError,
44
35
BzrBadParameterNotUnicode,
49
from bzrlib.symbol_versioning import *
50
39
from bzrlib.trace import mutter
51
import bzrlib.win32console
54
42
def make_readonly(filename):
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)
75
mode = os.lstat(f)[ST_MODE]
119
94
def kind_marker(kind):
120
95
if kind == 'file':
122
elif kind == _directory_kind:
97
elif kind == 'directory':
124
99
elif kind == 'symlink':
127
102
raise BzrError('invalid file kind %r' % kind)
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))
105
if hasattr(os.path, 'lexists'):
106
return os.path.lexists(f)
108
if hasattr(os, 'lstat'):
114
if e.errno == errno.ENOENT:
117
raise BzrError("lstat/stat of (%r): %r" % (f, e))
145
119
def fancy_rename(old, new, rename_func, unlink_func):
146
120
"""A fancy rename, when you don't have atomic rename.
197
171
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)
246
# Default is to just use the python builtins, but these can be rebound on
247
# particular platforms.
248
abspath = _posix_abspath
249
realpath = _posix_realpath
173
# Default is to just use the python builtins
174
abspath = os.path.abspath
175
realpath = os.path.realpath
250
176
pathjoin = os.path.join
251
177
normpath = os.path.normpath
252
178
getcwd = os.getcwdu
254
180
rename = os.rename
255
181
dirname = os.path.dirname
256
182
basename = os.path.basename
257
rmtree = shutil.rmtree
259
MIN_ABS_PATHLENGTH = 1
184
if os.name == "posix":
185
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
186
# choke on a Unicode string containing a relative path if
187
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
189
_fs_enc = sys.getfilesystemencoding()
191
return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
194
return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
262
196
if sys.platform == 'win32':
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
271
MIN_ABS_PATHLENGTH = 3
273
def _win32_delete_readonly(function, path, excinfo):
274
"""Error handler for shutil.rmtree function [for win32]
275
Helps to remove files and dirs marked as read-only.
277
type_, value = excinfo[:2]
278
if function in (os.remove, os.rmdir) \
279
and type_ == OSError \
280
and value.errno == errno.EACCES:
281
bzrlib.osutils.make_writable(path)
286
def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
287
"""Replacer for shutil.rmtree: could remove readonly dirs/files"""
288
return shutil.rmtree(path, ignore_errors, onerror)
197
# We need to use the Unicode-aware os.path.abspath and
198
# os.path.realpath on Windows systems.
200
return os.path.abspath(path).replace('\\', '/')
203
return os.path.realpath(path).replace('\\', '/')
206
return os.path.join(*args).replace('\\', '/')
209
return os.path.normpath(path).replace('\\', '/')
212
return os.getcwdu().replace('\\', '/')
214
def mkdtemp(*args, **kwargs):
215
return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
217
def rename(old, new):
218
fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
291
221
def normalizepath(f):
719
606
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
770
609
def terminal_width():
771
610
"""Return estimated terminal width."""
772
if sys.platform == 'win32':
773
import bzrlib.win32console
774
return bzrlib.win32console.get_console_size()[0]
612
# TODO: Do something smart on Windows?
614
# TODO: Is there anything that gets a better update when the window
615
# is resized while the program is running? We could use the Python termcap
777
import struct, fcntl, termios
778
s = struct.pack('HHHH', 0, 0, 0, 0)
779
x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
780
width = struct.unpack('HHHH', x)[1]
785
width = int(os.environ['COLUMNS'])
793
def supports_executable():
794
return sys.platform != "win32"
797
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
800
def check_legal_path(path):
801
"""Check whether the supplied path is legal.
802
This is only required on Windows, so we don't test on other platforms
805
if sys.platform != "win32":
807
if _validWin32PathRE.match(path) is None:
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:
618
return int(os.environ['COLUMNS'])
619
except (IndexError, KeyError, ValueError):