1
# Bazaar-NG -- distributed version control
 
 
3
# Copyright (C) 2005 by Canonical Ltd
 
 
5
# This program is free software; you can redistribute it and/or modify
 
 
6
# it under the terms of the GNU General Public License as published by
 
 
7
# the Free Software Foundation; either version 2 of the License, or
 
 
8
# (at your option) any later version.
 
 
10
# This program is distributed in the hope that it will be useful,
 
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
13
# GNU General Public License for more details.
 
 
15
# You should have received a copy of the GNU General Public License
 
 
16
# along with this program; if not, write to the Free Software
 
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
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,
 
 
43
from bzrlib.errors import (BzrError,
 
 
44
                           BzrBadParameterNotUnicode,
 
 
49
from bzrlib.symbol_versioning import *
 
 
50
from bzrlib.trace import mutter
 
 
51
import bzrlib.win32console
 
 
54
def make_readonly(filename):
 
 
55
    """Make a filename read-only."""
 
 
56
    mod = os.stat(filename).st_mode
 
 
58
    os.chmod(filename, mod)
 
 
61
def make_writable(filename):
 
 
62
    mod = os.stat(filename).st_mode
 
 
64
    os.chmod(filename, mod)
 
 
71
    """Return a quoted filename filename
 
 
73
    This previously used backslash quoting, but that works poorly on
 
 
75
    # TODO: I'm not really sure this is the best format either.x
 
 
78
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
 
 
80
    if _QUOTE_RE.search(f):
 
 
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)
 
 
119
def kind_marker(kind):
 
 
122
    elif kind == _directory_kind:
 
 
124
    elif kind == 'symlink':
 
 
127
        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))
 
 
145
def fancy_rename(old, new, rename_func, unlink_func):
 
 
146
    """A fancy rename, when you don't have atomic rename.
 
 
148
    :param old: The old path, to rename from
 
 
149
    :param new: The new path, to rename to
 
 
150
    :param rename_func: The potentially non-atomic rename function
 
 
151
    :param unlink_func: A way to delete the target file if the full rename succeeds
 
 
154
    # sftp rename doesn't allow overwriting, so play tricks:
 
 
156
    base = os.path.basename(new)
 
 
157
    dirname = os.path.dirname(new)
 
 
158
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
 
 
159
    tmp_name = pathjoin(dirname, tmp_name)
 
 
161
    # Rename the file out of the way, but keep track if it didn't exist
 
 
162
    # We don't want to grab just any exception
 
 
163
    # something like EACCES should prevent us from continuing
 
 
164
    # The downside is that the rename_func has to throw an exception
 
 
165
    # with an errno = ENOENT, or NoSuchFile
 
 
168
        rename_func(new, tmp_name)
 
 
169
    except (NoSuchFile,), e:
 
 
172
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
 
 
173
        # function raises an IOError with errno == None when a rename fails.
 
 
174
        # This then gets caught here.
 
 
175
        if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
 
 
178
        if (not hasattr(e, 'errno') 
 
 
179
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
 
 
186
        # This may throw an exception, in which case success will
 
 
188
        rename_func(old, new)
 
 
192
            # If the file used to exist, rename it back into place
 
 
193
            # otherwise just delete it from the tmp location
 
 
195
                unlink_func(tmp_name)
 
 
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)
 
 
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
 
 
250
pathjoin = os.path.join
 
 
251
normpath = os.path.normpath
 
 
253
mkdtemp = tempfile.mkdtemp
 
 
255
dirname = os.path.dirname
 
 
256
basename = os.path.basename
 
 
257
rmtree = shutil.rmtree
 
 
259
MIN_ABS_PATHLENGTH = 1
 
 
262
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)
 
 
291
def normalizepath(f):
 
 
292
    if hasattr(os.path, 'realpath'):
 
 
296
    [p,e] = os.path.split(f)
 
 
297
    if e == "" or e == "." or e == "..":
 
 
300
        return pathjoin(F(p), e)
 
 
304
    """Copy a file to a backup.
 
 
306
    Backups are named in GNU-style, with a ~ suffix.
 
 
308
    If the file is already a backup, it's not copied.
 
 
314
    if has_symlinks() and os.path.islink(fn):
 
 
315
        target = os.readlink(fn)
 
 
316
        os.symlink(target, bfn)
 
 
324
    outf = file(bfn, 'wb')
 
 
332
    """True if f is an accessible directory."""
 
 
334
        return S_ISDIR(os.lstat(f)[ST_MODE])
 
 
340
    """True if f is a regular file."""
 
 
342
        return S_ISREG(os.lstat(f)[ST_MODE])
 
 
347
    """True if f is a symlink."""
 
 
349
        return S_ISLNK(os.lstat(f)[ST_MODE])
 
 
353
def is_inside(dir, fname):
 
 
354
    """True if fname is inside dir.
 
 
356
    The parameters should typically be passed to osutils.normpath first, so
 
 
357
    that . and .. and repeated slashes are eliminated, and the separators
 
 
358
    are canonical for the platform.
 
 
360
    The empty string as a dir name is taken as top-of-tree and matches 
 
 
363
    >>> is_inside('src', pathjoin('src', 'foo.c'))
 
 
365
    >>> is_inside('src', 'srccontrol')
 
 
367
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
 
 
369
    >>> is_inside('foo.c', 'foo.c')
 
 
371
    >>> is_inside('foo.c', '')
 
 
373
    >>> is_inside('', 'foo.c')
 
 
376
    # XXX: Most callers of this can actually do something smarter by 
 
 
377
    # looking at the inventory
 
 
387
    return fname.startswith(dir)
 
 
390
def is_inside_any(dir_list, fname):
 
 
391
    """True if fname is inside any of given dirs."""
 
 
392
    for dirname in dir_list:
 
 
393
        if is_inside(dirname, fname):
 
 
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):
 
 
408
def pumpfile(fromfile, tofile):
 
 
409
    """Copy contents of one file to another."""
 
 
412
        b = fromfile.read(BUFSIZE)
 
 
418
def file_iterator(input_file, readsize=32768):
 
 
420
        b = input_file.read(readsize)
 
 
427
    if hasattr(f, 'tell'):
 
 
440
def sha_strings(strings):
 
 
441
    """Return the sha-1 of concatenation of strings"""
 
 
443
    map(s.update, strings)
 
 
453
def fingerprint_file(f):
 
 
458
    return {'size': size,
 
 
459
            'sha1': s.hexdigest()}
 
 
462
def compare_files(a, b):
 
 
463
    """Returns true if equal in contents"""
 
 
474
def local_time_offset(t=None):
 
 
475
    """Return offset of local zone from GMT, either at present or at time t."""
 
 
476
    # python2.3 localtime() can't take None
 
 
480
    if time.localtime(t).tm_isdst and time.daylight:
 
 
483
        return -time.timezone
 
 
486
def format_date(t, offset=0, timezone='original', date_fmt=None, 
 
 
488
    ## TODO: Perhaps a global option to use either universal or local time?
 
 
489
    ## Or perhaps just let people set $TZ?
 
 
490
    assert isinstance(t, float)
 
 
492
    if timezone == 'utc':
 
 
495
    elif timezone == 'original':
 
 
498
        tt = time.gmtime(t + offset)
 
 
499
    elif timezone == 'local':
 
 
500
        tt = time.localtime(t)
 
 
501
        offset = local_time_offset(t)
 
 
503
        raise BzrError("unsupported timezone format %r" % timezone,
 
 
504
                       ['options are "utc", "original", "local"'])
 
 
506
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
 
 
508
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
 
 
511
    return (time.strftime(date_fmt, tt) +  offset_str)
 
 
514
def compact_date(when):
 
 
515
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
 
 
520
    """Return size of given open file."""
 
 
521
    return os.fstat(f.fileno())[ST_SIZE]
 
 
524
# Define rand_bytes based on platform.
 
 
526
    # Python 2.4 and later have os.urandom,
 
 
527
    # but it doesn't work on some arches
 
 
529
    rand_bytes = os.urandom
 
 
530
except (NotImplementedError, AttributeError):
 
 
531
    # If python doesn't have os.urandom, or it doesn't work,
 
 
532
    # then try to first pull random data from /dev/urandom
 
 
533
    if os.path.exists("/dev/urandom"):
 
 
534
        rand_bytes = file('/dev/urandom', 'rb').read
 
 
535
    # Otherwise, use this hack as a last resort
 
 
537
        # not well seeded, but better than nothing
 
 
542
                s += chr(random.randint(0, 255))
 
 
547
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
 
 
549
    """Return a random string of num alphanumeric characters
 
 
551
    The result only contains lowercase chars because it may be used on 
 
 
552
    case-insensitive filesystems.
 
 
555
    for raw_byte in rand_bytes(num):
 
 
556
        s += ALNUM[ord(raw_byte) % 36]
 
 
560
## TODO: We could later have path objects that remember their list
 
 
561
## decomposition (might be too tricksy though.)
 
 
564
    """Turn string into list of parts.
 
 
570
    >>> splitpath('a/./b')
 
 
572
    >>> splitpath('a/.b')
 
 
574
    >>> splitpath('a/../b')
 
 
575
    Traceback (most recent call last):
 
 
577
    BzrError: sorry, '..' not allowed in path
 
 
579
    assert isinstance(p, types.StringTypes)
 
 
581
    # split on either delimiter because people might use either on
 
 
583
    ps = re.split(r'[\\/]', p)
 
 
588
            raise BzrError("sorry, %r not allowed in path" % f)
 
 
589
        elif (f == '.') or (f == ''):
 
 
596
    assert isinstance(p, list)
 
 
598
        if (f == '..') or (f == None) or (f == ''):
 
 
599
            raise BzrError("sorry, %r not allowed in path" % f)
 
 
603
@deprecated_function(zero_nine)
 
 
604
def appendpath(p1, p2):
 
 
608
        return pathjoin(p1, p2)
 
 
612
    """Split s into lines, but without removing the newline characters."""
 
 
613
    lines = s.split('\n')
 
 
614
    result = [line + '\n' for line in lines[:-1]]
 
 
616
        result.append(lines[-1])
 
 
620
def hardlinks_good():
 
 
621
    return sys.platform not in ('win32', 'cygwin', 'darwin')
 
 
624
def link_or_copy(src, dest):
 
 
625
    """Hardlink a file, or copy it if it can't be hardlinked."""
 
 
626
    if not hardlinks_good():
 
 
631
    except (OSError, IOError), e:
 
 
632
        if e.errno != errno.EXDEV:
 
 
636
def delete_any(full_path):
 
 
637
    """Delete a file or directory."""
 
 
641
    # We may be renaming a dangling inventory id
 
 
642
        if e.errno not in (errno.EISDIR, errno.EACCES, errno.EPERM):
 
 
648
    if hasattr(os, 'symlink'):
 
 
654
def contains_whitespace(s):
 
 
655
    """True if there are any whitespace characters in s."""
 
 
656
    for ch in string.whitespace:
 
 
663
def contains_linebreaks(s):
 
 
664
    """True if there is any vertical whitespace in s."""
 
 
672
def relpath(base, path):
 
 
673
    """Return path relative to base, or raise exception.
 
 
675
    The path may be either an absolute path or a path relative to the
 
 
676
    current working directory.
 
 
678
    os.path.commonprefix (python2.4) has a bad bug that it works just
 
 
679
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
 
 
683
    assert len(base) >= MIN_ABS_PATHLENGTH, ('Length of base must be equal or'
 
 
684
        ' exceed the platform minimum length (which is %d)' % 
 
 
691
    while len(head) >= len(base):
 
 
694
        head, tail = os.path.split(head)
 
 
698
        raise PathNotChild(rp, base)
 
 
706
def safe_unicode(unicode_or_utf8_string):
 
 
707
    """Coerce unicode_or_utf8_string into unicode.
 
 
709
    If it is unicode, it is returned.
 
 
710
    Otherwise it is decoded from utf-8. If a decoding error
 
 
711
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped 
 
 
712
    as a BzrBadParameter exception.
 
 
714
    if isinstance(unicode_or_utf8_string, unicode):
 
 
715
        return unicode_or_utf8_string
 
 
717
        return unicode_or_utf8_string.decode('utf8')
 
 
718
    except UnicodeDecodeError:
 
 
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
 
 
770
def terminal_width():
 
 
771
    """Return estimated terminal width."""
 
 
772
    if sys.platform == 'win32':
 
 
773
        import bzrlib.win32console
 
 
774
        return bzrlib.win32console.get_console_size()[0]
 
 
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: