1
# Bazaar -- distributed version control
3
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005, 2006 Canonical Ltd
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
17
from cStringIO import StringIO
21
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
22
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
26
from bzrlib.lazy_import import lazy_import
27
lazy_import(globals(), """
29
from datetime import datetime
21
31
from ntpath import (abspath as _nt_abspath,
24
34
realpath as _nt_realpath,
25
35
splitdrive as _nt_splitdrive,
28
from os import listdir
33
from shutil import copyfile
35
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
36
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
44
from tempfile import (
45
from bzrlib.errors import (BzrError,
46
BzrBadParameterNotUnicode,
51
from bzrlib.symbol_versioning import (deprecated_function,
56
from bzrlib.symbol_versioning import (
53
60
from bzrlib.trace import mutter
122
129
return _mapper(_lstat(f).st_mode)
123
130
except OSError, e:
124
131
if getattr(e, 'errno', None) == errno.ENOENT:
125
raise bzrlib.errors.NoSuchFile(f)
132
raise errors.NoSuchFile(f)
159
166
if e.errno == errno.ENOENT:
162
raise BzrError("lstat/stat of (%r): %r" % (f, e))
169
raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
165
172
def fancy_rename(old, new, rename_func, unlink_func):
186
193
file_existed = False
188
195
rename_func(new, tmp_name)
189
except (NoSuchFile,), e:
196
except (errors.NoSuchFile,), e:
191
198
except IOError, e:
192
199
# RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
221
228
# choke on a Unicode string containing a relative path if
222
229
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
224
_fs_enc = sys.getfilesystemencoding()
231
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
225
232
def _posix_abspath(path):
226
233
# jam 20060426 rather than encoding to fsencoding
227
234
# copy posixpath.abspath, but use os.getcwdu instead
302
309
pathjoin = os.path.join
303
310
normpath = os.path.normpath
304
311
getcwd = os.getcwdu
305
mkdtemp = tempfile.mkdtemp
306
312
rename = os.rename
307
313
dirname = os.path.dirname
308
314
basename = os.path.basename
309
rmtree = shutil.rmtree
315
split = os.path.split
316
splitext = os.path.splitext
317
# These were already imported into local scope
318
# mkdtemp = tempfile.mkdtemp
319
# rmtree = shutil.rmtree
311
321
MIN_ABS_PATHLENGTH = 1
326
336
"""Error handler for shutil.rmtree function [for win32]
327
337
Helps to remove files and dirs marked as read-only.
329
type_, value = excinfo[:2]
339
exception = excinfo[1]
330
340
if function in (os.remove, os.rmdir) \
331
and type_ == OSError \
332
and value.errno == errno.EACCES:
333
bzrlib.osutils.make_writable(path)
341
and isinstance(exception, OSError) \
342
and exception.errno == errno.EACCES:
366
376
mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
368
378
mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
379
if output_encoding == 'cp0':
380
# invalid encoding (cp0 means 'no codepage' on Windows)
381
output_encoding = bzrlib.user_encoding
382
mutter('cp0 is invalid encoding.'
383
' encoding stdout as bzrlib.user_encoding %r', output_encoding)
386
codecs.lookup(output_encoding)
388
sys.stderr.write('bzr: warning:'
389
' unknown terminal encoding %s.\n'
390
' Using encoding %s instead.\n'
391
% (output_encoding, bzrlib.user_encoding)
393
output_encoding = bzrlib.user_encoding
369
395
return output_encoding
441
467
The empty string as a dir name is taken as top-of-tree and matches
444
>>> is_inside('src', pathjoin('src', 'foo.c'))
446
>>> is_inside('src', 'srccontrol')
448
>>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
450
>>> is_inside('foo.c', 'foo.c')
452
>>> is_inside('foo.c', '')
454
>>> is_inside('', 'foo.c')
457
470
# XXX: Most callers of this can actually do something smarter by
458
471
# looking at the inventory
555
568
def local_time_offset(t=None):
556
569
"""Return offset of local zone from GMT, either at present or at time t."""
557
# python2.3 localtime() can't take None
561
if time.localtime(t).tm_isdst and time.daylight:
564
return -time.timezone
572
offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
573
return offset.days * 86400 + offset.seconds
567
576
def format_date(t, offset=0, timezone='original', date_fmt=None,
581
590
tt = time.localtime(t)
582
591
offset = local_time_offset(t)
584
raise BzrError("unsupported timezone format %r" % timezone,
585
['options are "utc", "original", "local"'])
593
raise errors.BzrError("unsupported timezone format %r" % timezone,
594
['options are "utc", "original", "local"'])
586
595
if date_fmt is None:
587
596
date_fmt = "%a %Y-%m-%d %H:%M:%S"
596
605
return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
608
def format_delta(delta):
609
"""Get a nice looking string for a time delta.
611
:param delta: The time difference in seconds, can be positive or negative.
612
positive indicates time in the past, negative indicates time in the
613
future. (usually time.time() - stored_time)
614
:return: String formatted to show approximate resolution
620
direction = 'in the future'
624
if seconds < 90: # print seconds up to 90 seconds
626
return '%d second %s' % (seconds, direction,)
628
return '%d seconds %s' % (seconds, direction)
630
minutes = int(seconds / 60)
631
seconds -= 60 * minutes
636
if minutes < 90: # print minutes, seconds up to 90 minutes
638
return '%d minute, %d second%s %s' % (
639
minutes, seconds, plural_seconds, direction)
641
return '%d minutes, %d second%s %s' % (
642
minutes, seconds, plural_seconds, direction)
644
hours = int(minutes / 60)
645
minutes -= 60 * hours
652
return '%d hour, %d minute%s %s' % (hours, minutes,
653
plural_minutes, direction)
654
return '%d hours, %d minute%s %s' % (hours, minutes,
655
plural_minutes, direction)
601
658
"""Return size of given open file."""
611
668
except (NotImplementedError, AttributeError):
612
669
# If python doesn't have os.urandom, or it doesn't work,
613
670
# then try to first pull random data from /dev/urandom
614
if os.path.exists("/dev/urandom"):
615
672
rand_bytes = file('/dev/urandom', 'rb').read
616
673
# Otherwise, use this hack as a last resort
674
except (IOError, OSError):
618
675
# not well seeded, but better than nothing
619
676
def rand_bytes(n):
642
699
## decomposition (might be too tricksy though.)
644
701
def splitpath(p):
645
"""Turn string into list of parts.
651
>>> splitpath('a/./b')
653
>>> splitpath('a/.b')
655
>>> splitpath('a/../b')
656
Traceback (most recent call last):
658
BzrError: sorry, '..' not allowed in path
660
assert isinstance(p, types.StringTypes)
702
"""Turn string into list of parts."""
703
assert isinstance(p, basestring)
662
705
# split on either delimiter because people might use either on
677
720
assert isinstance(p, list)
679
722
if (f == '..') or (f is None) or (f == ''):
680
raise BzrError("sorry, %r not allowed in path" % f)
723
raise errors.BzrError("sorry, %r not allowed in path" % f)
681
724
return pathjoin(*p)
705
748
def link_or_copy(src, dest):
706
749
"""Hardlink a file, or copy it if it can't be hardlinked."""
707
750
if not hardlinks_good():
751
shutil.copyfile(src, dest)
711
754
os.link(src, dest)
712
755
except (OSError, IOError), e:
713
756
if e.errno != errno.EXDEV:
758
shutil.copyfile(src, dest)
717
760
def delete_any(full_path):
718
761
"""Delete a file or directory."""
735
778
def contains_whitespace(s):
736
779
"""True if there are any whitespace characters in s."""
737
for ch in string.whitespace:
780
# string.whitespace can include '\xa0' in certain locales, because it is
781
# considered "non-breaking-space" as part of ISO-8859-1. But it
782
# 1) Isn't a breaking whitespace
783
# 2) Isn't one of ' \t\r\n' which are characters we sometimes use as
785
# 3) '\xa0' isn't unicode safe since it is >128.
786
# So we are following textwrap's example and hard-coding our own.
787
# We probably could ignore \v and \f, too.
788
for ch in u' \t\n\r\v\f':
798
849
return unicode_or_utf8_string.decode('utf8')
799
850
except UnicodeDecodeError:
800
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
851
raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
803
854
_platform_normalizes_filenames = False
847
898
def terminal_width():
848
899
"""Return estimated terminal width."""
849
900
if sys.platform == 'win32':
850
import bzrlib.win32console
851
return bzrlib.win32console.get_console_size()[0]
901
return win32utils.get_console_size()[0]
854
904
import struct, fcntl, termios
872
922
return sys.platform != "win32"
925
def supports_posix_readonly():
926
"""Return True if 'readonly' has POSIX semantics, False otherwise.
928
Notably, a win32 readonly file cannot be deleted, unlike POSIX where the
929
directory controls creation/deletion, etc.
931
And under win32, readonly means that the directory itself cannot be
932
deleted. The contents of a readonly directory can be changed, unlike POSIX
933
where files in readonly directories cannot be added, deleted or renamed.
935
return sys.platform != "win32"
875
938
def set_or_unset_env(env_variable, value):
876
939
"""Modify the environment, setting or removing the env_variable.
902
965
if sys.platform != "win32":
904
967
if _validWin32PathRE.match(path) is None:
905
raise IllegalPath(path)
968
raise errors.IllegalPath(path)
908
971
def walkdirs(top, prefix=""):
1025
1088
_cached_user_encoding = None
1028
def get_user_encoding():
1091
def get_user_encoding(use_cache=True):
1029
1092
"""Find out what the preferred user encoding is.
1031
1094
This is generally the encoding that is used for command line parameters
1032
1095
and file contents. This may be different from the terminal encoding
1033
1096
or the filesystem encoding.
1098
:param use_cache: Enable cache for detected encoding.
1099
(This parameter is turned on by default,
1100
and required only for selftesting)
1035
1102
:return: A string defining the preferred user encoding
1037
1104
global _cached_user_encoding
1038
if _cached_user_encoding is not None:
1105
if _cached_user_encoding is not None and use_cache:
1039
1106
return _cached_user_encoding
1041
1108
if sys.platform == 'darwin':
1057
1124
' doesn\'t support the locale set by $LANG (%s)\n'
1058
1125
" Continuing with ascii encoding.\n"
1059
1126
% (e, os.environ.get('LANG')))
1061
if _cached_user_encoding is None:
1062
_cached_user_encoding = 'ascii'
1063
return _cached_user_encoding
1127
user_encoding = 'ascii'
1129
# Windows returns 'cp0' to indicate there is no code page. So we'll just
1130
# treat that as ASCII, and not support printing unicode characters to the
1132
if user_encoding in (None, 'cp0'):
1133
user_encoding = 'ascii'
1137
codecs.lookup(user_encoding)
1139
sys.stderr.write('bzr: warning:'
1140
' unknown encoding %s.'
1141
' Continuing with ascii encoding.\n'
1144
user_encoding = 'ascii'
1147
_cached_user_encoding = user_encoding
1149
return user_encoding
1152
def recv_all(socket, bytes):
1153
"""Receive an exact number of bytes.
1155
Regular Socket.recv() may return less than the requested number of bytes,
1156
dependning on what's in the OS buffer. MSG_WAITALL is not available
1157
on all platforms, but this should work everywhere. This will return
1158
less than the requested amount if the remote end closes.
1160
This isn't optimized and is intended mostly for use in testing.
1163
while len(b) < bytes:
1164
new = socket.recv(bytes - len(b))
1170
def dereference_path(path):
1171
"""Determine the real path to a file.
1173
All parent elements are dereferenced. But the file itself is not
1175
:param path: The original path. May be absolute or relative.
1176
:return: the real path *to* the file
1178
parent, base = os.path.split(path)
1179
# The pathjoin for '.' is a workaround for Python bug #1213894.
1180
# (initial path components aren't dereferenced)
1181
return pathjoin(realpath(pathjoin('.', parent)), base)