1
1
# Bazaar-NG -- distributed version control
3
3
# Copyright (C) 2005 by Canonical Ltd
5
5
# This program is free software; you can redistribute it and/or modify
6
6
# it under the terms of the GNU General Public License as published by
7
7
# the Free Software Foundation; either version 2 of the License, or
8
8
# (at your option) any later version.
10
10
# This program is distributed in the hope that it will be useful,
11
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
13
# GNU General Public License for more details.
15
15
# You should have received a copy of the GNU General Public License
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
import os, types, re, time, errno, sys
20
from stat import S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE
19
from cStringIO import StringIO
21
from ntpath import (abspath as _nt_abspath,
23
normpath as _nt_normpath,
24
realpath as _nt_realpath,
27
from os import listdir
32
from shutil import copyfile
34
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
35
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
22
from bzrlib.errors import BzrError
44
from bzrlib.errors import (BzrError,
45
BzrBadParameterNotUnicode,
50
from bzrlib.symbol_versioning import *
23
51
from bzrlib.trace import mutter
52
import bzrlib.win32console
26
55
def make_readonly(filename):
27
56
"""Make a filename read-only."""
28
# TODO: probably needs to be fixed for windows
29
57
mod = os.stat(filename).st_mode
30
58
mod = mod & 0777555
31
59
os.chmod(filename, mod)
37
65
os.chmod(filename, mod)
40
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
42
72
"""Return a quoted filename filename
44
74
This previously used backslash quoting, but that works poorly on
46
76
# TODO: I'm not really sure this is the best format either.x
79
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
47
81
if _QUOTE_RE.search(f):
48
82
return '"' + f + '"'
54
mode = os.lstat(f)[ST_MODE]
62
raise BzrError("can't handle file kind with mode %o of %r" % (mode, f))
87
_directory_kind = 'directory'
90
stat.S_IFDIR:_directory_kind,
91
stat.S_IFCHR:'chardev',
95
stat.S_IFLNK:'symlink',
96
stat.S_IFSOCK:'socket',
100
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
101
"""Generate a file kind from a stat mode. This is used in walkdirs.
103
Its performance is critical: Do not mutate without careful benchmarking.
106
return _formats[stat_mode & 0170000]
111
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
112
return _mapper(_lstat(f).st_mode)
65
115
def kind_marker(kind):
66
116
if kind == 'file':
68
elif kind == 'directory':
118
elif kind == _directory_kind:
70
120
elif kind == 'symlink':
73
123
raise BzrError('invalid file kind %r' % kind)
125
lexists = getattr(os.path, 'lexists', None)
129
if hasattr(os, 'lstat'):
135
if e.errno == errno.ENOENT:
138
raise BzrError("lstat/stat of (%r): %r" % (f, e))
141
def fancy_rename(old, new, rename_func, unlink_func):
142
"""A fancy rename, when you don't have atomic rename.
144
:param old: The old path, to rename from
145
:param new: The new path, to rename to
146
:param rename_func: The potentially non-atomic rename function
147
:param unlink_func: A way to delete the target file if the full rename succeeds
150
# sftp rename doesn't allow overwriting, so play tricks:
152
base = os.path.basename(new)
153
dirname = os.path.dirname(new)
154
tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
155
tmp_name = pathjoin(dirname, tmp_name)
157
# Rename the file out of the way, but keep track if it didn't exist
158
# We don't want to grab just any exception
159
# something like EACCES should prevent us from continuing
160
# The downside is that the rename_func has to throw an exception
161
# with an errno = ENOENT, or NoSuchFile
164
rename_func(new, tmp_name)
165
except (NoSuchFile,), e:
168
# RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
169
# function raises an IOError with errno == None when a rename fails.
170
# This then gets caught here.
171
if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
174
if (not hasattr(e, 'errno')
175
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
182
# This may throw an exception, in which case success will
184
rename_func(old, new)
188
# If the file used to exist, rename it back into place
189
# otherwise just delete it from the tmp location
191
unlink_func(tmp_name)
193
rename_func(tmp_name, new)
196
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
197
# choke on a Unicode string containing a relative path if
198
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
200
_fs_enc = sys.getfilesystemencoding()
201
def _posix_abspath(path):
202
# jam 20060426 rather than encoding to fsencoding
203
# copy posixpath.abspath, but use os.getcwdu instead
204
if not posixpath.isabs(path):
205
path = posixpath.join(getcwd(), path)
206
return posixpath.normpath(path)
209
def _posix_realpath(path):
210
return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
213
def _win32_abspath(path):
214
# Real _nt_abspath doesn't have a problem with a unicode cwd
215
return _nt_abspath(unicode(path)).replace('\\', '/')
218
def _win32_realpath(path):
219
# Real _nt_realpath doesn't have a problem with a unicode cwd
220
return _nt_realpath(unicode(path)).replace('\\', '/')
223
def _win32_pathjoin(*args):
224
return _nt_join(*args).replace('\\', '/')
227
def _win32_normpath(path):
228
return _nt_normpath(unicode(path)).replace('\\', '/')
232
return os.getcwdu().replace('\\', '/')
235
def _win32_mkdtemp(*args, **kwargs):
236
return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
239
def _win32_rename(old, new):
240
fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
243
# Default is to just use the python builtins, but these can be rebound on
244
# particular platforms.
245
abspath = _posix_abspath
246
realpath = _posix_realpath
247
pathjoin = os.path.join
248
normpath = os.path.normpath
250
mkdtemp = tempfile.mkdtemp
252
dirname = os.path.dirname
253
basename = os.path.basename
254
rmtree = shutil.rmtree
256
MIN_ABS_PATHLENGTH = 1
259
if sys.platform == 'win32':
260
abspath = _win32_abspath
261
realpath = _win32_realpath
262
pathjoin = _win32_pathjoin
263
normpath = _win32_normpath
264
getcwd = _win32_getcwd
265
mkdtemp = _win32_mkdtemp
266
rename = _win32_rename
268
MIN_ABS_PATHLENGTH = 3
270
def _win32_delete_readonly(function, path, excinfo):
271
"""Error handler for shutil.rmtree function [for win32]
272
Helps to remove files and dirs marked as read-only.
274
type_, value = excinfo[:2]
275
if function in (os.remove, os.rmdir) \
276
and type_ == OSError \
277
and value.errno == errno.EACCES:
278
bzrlib.osutils.make_writable(path)
283
def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
284
"""Replacer for shutil.rmtree: could remove readonly dirs/files"""
285
return shutil.rmtree(path, ignore_errors, onerror)
288
def get_terminal_encoding():
289
"""Find the best encoding for printing to the screen.
291
This attempts to check both sys.stdout and sys.stdin to see
292
what encoding they are in, and if that fails it falls back to
293
bzrlib.user_encoding.
294
The problem is that on Windows, locale.getpreferredencoding()
295
is not the same encoding as that used by the console:
296
http://mail.python.org/pipermail/python-list/2003-May/162357.html
298
On my standard US Windows XP, the preferred encoding is
299
cp1252, but the console is cp437
301
output_encoding = getattr(sys.stdout, 'encoding', None)
302
if not output_encoding:
303
input_encoding = getattr(sys.stdin, 'encoding', None)
304
if not input_encoding:
305
output_encoding = bzrlib.user_encoding
306
mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
308
output_encoding = input_encoding
309
mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
311
mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
312
return output_encoding
315
def normalizepath(f):
316
if hasattr(os.path, 'realpath'):
320
[p,e] = os.path.split(f)
321
if e == "" or e == "." or e == "..":
324
return pathjoin(F(p), e)
77
327
def backup_file(fn):
193
483
'sha1': s.hexdigest()}
197
"""Return per-user configuration directory.
199
By default this is ~/.bzr.conf/
201
TODO: Global option --config-dir to override this.
203
return os.path.expanduser("~/.bzr.conf")
207
"""Calculate automatic user identification.
209
Returns (realname, email).
211
Only used when none is set in the environment or the id file.
213
This previously used the FQDN as the default domain, but that can
214
be very slow on machines where DNS is broken. So now we simply
219
# XXX: Any good way to get real user name on win32?
224
w = pwd.getpwuid(uid)
225
gecos = w.pw_gecos.decode(bzrlib.user_encoding)
226
username = w.pw_name.decode(bzrlib.user_encoding)
227
comma = gecos.find(',')
231
realname = gecos[:comma]
237
realname = username = getpass.getuser().decode(bzrlib.user_encoding)
239
return realname, (username + '@' + socket.gethostname())
243
"""Return the full user id from a file or environment variable.
245
TODO: Allow taking this from a file in the branch directory too
246
for per-branch ids."""
247
v = os.environ.get('BZREMAIL')
249
return v.decode(bzrlib.user_encoding)
252
return (open(os.path.join(config_dir(), "email"))
254
.decode(bzrlib.user_encoding)
257
if e.errno != errno.ENOENT:
260
v = os.environ.get('EMAIL')
262
return v.decode(bzrlib.user_encoding)
268
"""Return email-style username.
270
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
272
TODO: Check it's reasonably well-formed.
278
name, email = _auto_user_id()
280
return '%s <%s>' % (name, email)
285
_EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+')
287
"""Return just the email component of a username."""
290
m = _EMAIL_RE.search(e)
292
raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
295
return _auto_user_id()[1]
299
486
def compare_files(a, b):
300
487
"""Returns true if equal in contents"""
410
622
if (f == '..') or (f == None) or (f == ''):
411
623
raise BzrError("sorry, %r not allowed in path" % f)
412
return os.path.join(*p)
627
@deprecated_function(zero_nine)
415
628
def appendpath(p1, p2):
419
return os.path.join(p1, p2)
632
return pathjoin(p1, p2)
422
def extern_command(cmd, ignore_errors = False):
423
mutter('external command: %s' % `cmd`)
425
if not ignore_errors:
426
raise BzrError('command failed')
429
def _read_config_value(name):
430
"""Read a config value from the file ~/.bzr.conf/<name>
431
Return None if the file does not exist"""
433
f = file(os.path.join(config_dir(), name), "r")
434
return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
436
if e.errno == errno.ENOENT:
442
"""Return a sequence of possible editor binaries for the current platform"""
443
e = _read_config_value("editor")
636
"""Split s into lines, but without removing the newline characters."""
637
lines = s.split('\n')
638
result = [line + '\n' for line in lines[:-1]]
640
result.append(lines[-1])
644
def hardlinks_good():
645
return sys.platform not in ('win32', 'cygwin', 'darwin')
648
def link_or_copy(src, dest):
649
"""Hardlink a file, or copy it if it can't be hardlinked."""
650
if not hardlinks_good():
655
except (OSError, IOError), e:
656
if e.errno != errno.EXDEV:
660
def delete_any(full_path):
661
"""Delete a file or directory."""
665
# We may be renaming a dangling inventory id
666
if e.errno not in (errno.EISDIR, errno.EACCES, errno.EPERM):
672
if hasattr(os, 'symlink'):
447
if os.name == "windows":
449
elif os.name == "posix":
678
def contains_whitespace(s):
679
"""True if there are any whitespace characters in s."""
680
for ch in string.whitespace:
687
def contains_linebreaks(s):
688
"""True if there is any vertical whitespace in s."""
696
def relpath(base, path):
697
"""Return path relative to base, or raise exception.
699
The path may be either an absolute path or a path relative to the
700
current working directory.
702
os.path.commonprefix (python2.4) has a bad bug that it works just
703
on string prefixes, assuming that '/u' is a prefix of '/u2'. This
707
assert len(base) >= MIN_ABS_PATHLENGTH, ('Length of base must be equal or'
708
' exceed the platform minimum length (which is %d)' %
715
while len(head) >= len(base):
718
head, tail = os.path.split(head)
722
raise PathNotChild(rp, base)
730
def safe_unicode(unicode_or_utf8_string):
731
"""Coerce unicode_or_utf8_string into unicode.
733
If it is unicode, it is returned.
734
Otherwise it is decoded from utf-8. If a decoding error
735
occurs, it is wrapped as a If the decoding fails, the exception is wrapped
736
as a BzrBadParameter exception.
738
if isinstance(unicode_or_utf8_string, unicode):
739
return unicode_or_utf8_string
741
return unicode_or_utf8_string.decode('utf8')
742
except UnicodeDecodeError:
743
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
746
_platform_normalizes_filenames = False
747
if sys.platform == 'darwin':
748
_platform_normalizes_filenames = True
751
def normalizes_filenames():
752
"""Return True if this platform normalizes unicode filenames.
754
Mac OSX does, Windows/Linux do not.
756
return _platform_normalizes_filenames
759
if _platform_normalizes_filenames:
760
def unicode_filename(path):
761
"""Make sure 'path' is a properly normalized filename.
763
On platforms where the system normalizes filenames (Mac OSX),
764
you can access a file by any path which will normalize
766
Internally, bzr only supports NFC/NFKC normalization, since
767
that is the standard for XML documents.
768
So we return an normalized path, and indicate this has been
771
:return: (path, is_normalized) Return a path which can
772
access the file, and whether or not this path is
775
return unicodedata.normalize('NFKC', path), True
777
def unicode_filename(path):
778
"""Make sure 'path' is a properly normalized filename.
780
On platforms where the system does not normalize filenames
781
(Windows, Linux), you have to access a file by its exact path.
782
Internally, bzr only supports NFC/NFKC normalization, since
783
that is the standard for XML documents.
784
So we return the original path, and indicate if this is
787
:return: (path, is_normalized) Return a path which can
788
access the file, and whether or not this path is
791
return path, unicodedata.normalize('NFKC', path) == path
794
def terminal_width():
795
"""Return estimated terminal width."""
796
if sys.platform == 'win32':
797
import bzrlib.win32console
798
return bzrlib.win32console.get_console_size()[0]
801
import struct, fcntl, termios
802
s = struct.pack('HHHH', 0, 0, 0, 0)
803
x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
804
width = struct.unpack('HHHH', x)[1]
451
yield os.environ["EDITOR"]
456
def _run_editor(filename):
457
"""Try to execute an editor to edit the commit message. Returns True on success,
459
for e in _get_editor():
460
x = os.spawnvp(os.P_WAIT, e, (e, filename))
467
raise BzrError("Could not start any editor. Please specify $EDITOR or use ~/.bzr.conf/editor")
471
def get_text_message(infotext, ignoreline = "default"):
474
if ignoreline == "default":
475
ignoreline = "-- This line and the following will be ignored --"
478
tmp_fileno, msgfilename = tempfile.mkstemp()
479
msgfile = os.close(tmp_fileno)
480
if infotext is not None and infotext != "":
482
msgfile = file(msgfilename, "w")
483
msgfile.write("\n\n%s\n\n%s" % (ignoreline, infotext))
488
if not _run_editor(msgfilename):
493
lastline, nlines = 0, 0
494
for line in file(msgfilename, "r"):
495
stripped_line = line.strip()
496
# strip empty line before the log message starts
498
if stripped_line != "":
502
# check for the ignore line only if there
503
# is additional information at the end
504
if hasinfo and stripped_line == ignoreline:
507
# keep track of the last line that had some content
508
if stripped_line != "":
514
# delete empty lines at the end
516
# add a newline at the end, if needed
517
if not msg[-1].endswith("\n"):
518
return "%s%s" % ("".join(msg), "\n")
522
# delete the msg file in any case
523
try: os.unlink(msgfilename)
809
width = int(os.environ['COLUMNS'])
817
def supports_executable():
818
return sys.platform != "win32"
821
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
824
def check_legal_path(path):
825
"""Check whether the supplied path is legal.
826
This is only required on Windows, so we don't test on other platforms
829
if sys.platform != "win32":
831
if _validWin32PathRE.match(path) is None:
832
raise IllegalPath(path)
836
"""Yield data about all the directories in a tree.
838
This yields all the data about the contents of a directory at a time.
839
After each directory has been yielded, if the caller has mutated the list
840
to exclude some directories, they are then not descended into.
842
The data yielded is of the form:
843
[(relpath, basename, kind, lstat, path_from_top), ...]
845
:return: an iterator over the dirs.
849
_directory = _directory_kind
851
pending = [("", "", _directory, None, top)]
854
currentdir = pending.pop()
855
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
858
relroot = currentdir[0] + '/'
861
for name in sorted(_listdir(top)):
862
abspath = top + '/' + name
863
statvalue = lstat(abspath)
864
dirblock.append ((relroot + name, name, file_kind_from_stat_mode(statvalue.st_mode), statvalue, abspath))
866
# push the user specified dirs from dirblock
867
for dir in reversed(dirblock):
868
if dir[2] == _directory: