33
from bzrlib.errors import BzrError, NotBranchError
35
from bzrlib.errors import (BzrError,
36
BzrBadParameterNotUnicode,
34
41
from bzrlib.trace import mutter
42
import bzrlib.win32console
37
45
def make_readonly(filename):
110
120
raise BzrError("lstat/stat of (%r): %r" % (f, e))
112
def normalizepath(f):
113
if hasattr(os.path, 'realpath'):
117
[p,e] = os.path.split(f)
118
if e == "" or e == "." or e == "..":
121
return os.path.join(F(p), e)
122
def fancy_rename(old, new, rename_func, unlink_func):
123
"""A fancy rename, when you don't have atomic rename.
125
:param old: The old path, to rename from
126
:param new: The new path, to rename to
127
:param rename_func: The potentially non-atomic rename function
128
:param unlink_func: A way to delete the target file if the full rename succeeds
131
# sftp rename doesn't allow overwriting, so play tricks:
133
base = os.path.basename(new)
134
dirname = os.path.dirname(new)
135
tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
136
tmp_name = pathjoin(dirname, tmp_name)
138
# Rename the file out of the way, but keep track if it didn't exist
139
# We don't want to grab just any exception
140
# something like EACCES should prevent us from continuing
141
# The downside is that the rename_func has to throw an exception
142
# with an errno = ENOENT, or NoSuchFile
145
rename_func(new, tmp_name)
146
except (NoSuchFile,), e:
149
# RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
150
# function raises an IOError with errno == None when a rename fails.
151
# This then gets caught here.
152
if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
155
if (not hasattr(e, 'errno')
156
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
163
# This may throw an exception, in which case success will
165
rename_func(old, new)
169
# If the file used to exist, rename it back into place
170
# otherwise just delete it from the tmp location
172
unlink_func(tmp_name)
174
rename_func(tmp_name, new)
176
# Default is to just use the python builtins, but these can be rebound on
177
# particular platforms.
178
abspath = os.path.abspath
179
realpath = os.path.realpath
180
pathjoin = os.path.join
181
normpath = os.path.normpath
183
mkdtemp = tempfile.mkdtemp
185
dirname = os.path.dirname
186
basename = os.path.basename
187
rmtree = shutil.rmtree
189
MIN_ABS_PATHLENGTH = 1
123
191
if os.name == "posix":
124
192
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
128
196
_fs_enc = sys.getfilesystemencoding()
129
197
def abspath(path):
130
198
return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
131
200
def realpath(path):
132
201
return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
203
if sys.platform == 'win32':
134
204
# We need to use the Unicode-aware os.path.abspath and
135
205
# os.path.realpath on Windows systems.
136
abspath = os.path.abspath
137
realpath = os.path.realpath
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)
227
MIN_ABS_PATHLENGTH = 3
229
def _win32_delete_readonly(function, path, excinfo):
230
"""Error handler for shutil.rmtree function [for win32]
231
Helps to remove files and dirs marked as read-only.
233
type_, value = excinfo[:2]
234
if function in (os.remove, os.rmdir) \
235
and type_ == OSError \
236
and value.errno == errno.EACCES:
237
bzrlib.osutils.make_writable(path)
242
def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
243
"""Replacer for shutil.rmtree: could remove readonly dirs/files"""
244
return shutil.rmtree(path, ignore_errors, onerror)
247
def normalizepath(f):
248
if hasattr(os.path, 'realpath'):
252
[p,e] = os.path.split(f)
253
if e == "" or e == "." or e == "..":
256
return pathjoin(F(p), e)
139
259
def backup_file(fn):
140
260
"""Copy a file to a backup.
195
309
def is_inside(dir, fname):
196
310
"""True if fname is inside dir.
198
The parameters should typically be passed to os.path.normpath first, so
312
The parameters should typically be passed to osutils.normpath first, so
199
313
that . and .. and repeated slashes are eliminated, and the separators
200
314
are canonical for the platform.
202
316
The empty string as a dir name is taken as top-of-tree and matches
205
>>> is_inside('src', os.path.join('src', 'foo.c'))
319
>>> is_inside('src', pathjoin('src', 'foo.c'))
207
321
>>> is_inside('src', 'srccontrol')
209
>>> is_inside('src', os.path.join('src', 'a', 'a', 'a', 'foo.c'))
323
>>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
211
325
>>> is_inside('foo.c', 'foo.c')
494
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
496
"""Return a random string of num alphanumeric characters
498
The result only contains lowercase chars because it may be used on
499
case-insensitive filesystems.
502
for raw_byte in rand_bytes(num):
503
s += ALNUM[ord(raw_byte) % 36]
365
507
## TODO: We could later have path objects that remember their list
366
508
## decomposition (might be too tricksy though.)
403
545
if (f == '..') or (f == None) or (f == ''):
404
546
raise BzrError("sorry, %r not allowed in path" % f)
405
return os.path.join(*p)
408
550
def appendpath(p1, p2):
412
return os.path.join(p1, p2)
554
return pathjoin(p1, p2)
415
557
def split_lines(s):
416
558
"""Split s into lines, but without removing the newline characters."""
417
return StringIO(s).readlines()
559
lines = s.split('\n')
560
result = [line + '\n' for line in lines[:-1]]
562
result.append(lines[-1])
420
566
def hardlinks_good():
482
643
# XXX This should raise a NotChildPath exception, as its not tied
483
644
# to branch anymore.
484
raise NotBranchError("path %r is not within branch %r" % (rp, base))
486
return os.sep.join(s)
645
raise PathNotChild(rp, base)
653
def safe_unicode(unicode_or_utf8_string):
654
"""Coerce unicode_or_utf8_string into unicode.
656
If it is unicode, it is returned.
657
Otherwise it is decoded from utf-8. If a decoding error
658
occurs, it is wrapped as a If the decoding fails, the exception is wrapped
659
as a BzrBadParameter exception.
661
if isinstance(unicode_or_utf8_string, unicode):
662
return unicode_or_utf8_string
664
return unicode_or_utf8_string.decode('utf8')
665
except UnicodeDecodeError:
666
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
669
def terminal_width():
670
"""Return estimated terminal width."""
671
if sys.platform == 'win32':
672
import bzrlib.win32console
673
return bzrlib.win32console.get_console_size()[0]
676
import struct, fcntl, termios
677
s = struct.pack('HHHH', 0, 0, 0, 0)
678
x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
679
width = struct.unpack('HHHH', x)[1]
684
width = int(os.environ['COLUMNS'])
692
def supports_executable():
693
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
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
709
def check_legal_path(path):
710
"""Check whether the supplied path is legal.
711
This is only required on Windows, so we don't test on other platforms
714
if sys.platform != "win32":
716
if _validWin32PathRE.match(path) is None:
717
raise IllegalPath(path)