1
1
# Bazaar -- distributed version control
3
# Copyright (C) 2005 by Canonical Ltd
3
# Copyright (C) 2005 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
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
19
from cStringIO import StringIO
23
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
24
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
28
from bzrlib.lazy_import import lazy_import
29
lazy_import(globals(), """
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)
45
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
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
# These were already imported into local scope
316
# mkdtemp = tempfile.mkdtemp
317
# rmtree = shutil.rmtree
311
319
MIN_ABS_PATHLENGTH = 1
441
449
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
452
# XXX: Most callers of this can actually do something smarter by
458
453
# looking at the inventory
581
576
tt = time.localtime(t)
582
577
offset = local_time_offset(t)
584
raise BzrError("unsupported timezone format %r" % timezone,
585
['options are "utc", "original", "local"'])
579
raise errors.BzrError("unsupported timezone format %r" % timezone,
580
['options are "utc", "original", "local"'])
586
581
if date_fmt is None:
587
582
date_fmt = "%a %Y-%m-%d %H:%M:%S"
596
591
return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
594
def format_delta(delta):
595
"""Get a nice looking string for a time delta.
597
:param delta: The time difference in seconds, can be positive or negative.
598
positive indicates time in the past, negative indicates time in the
599
future. (usually time.time() - stored_time)
600
:return: String formatted to show approximate resolution
606
direction = 'in the future'
610
if seconds < 90: # print seconds up to 90 seconds
612
return '%d second %s' % (seconds, direction,)
614
return '%d seconds %s' % (seconds, direction)
616
minutes = int(seconds / 60)
617
seconds -= 60 * minutes
622
if minutes < 90: # print minutes, seconds up to 90 minutes
624
return '%d minute, %d second%s %s' % (
625
minutes, seconds, plural_seconds, direction)
627
return '%d minutes, %d second%s %s' % (
628
minutes, seconds, plural_seconds, direction)
630
hours = int(minutes / 60)
631
minutes -= 60 * hours
638
return '%d hour, %d minute%s %s' % (hours, minutes,
639
plural_minutes, direction)
640
return '%d hours, %d minute%s %s' % (hours, minutes,
641
plural_minutes, direction)
601
644
"""Return size of given open file."""
611
654
except (NotImplementedError, AttributeError):
612
655
# If python doesn't have os.urandom, or it doesn't work,
613
656
# then try to first pull random data from /dev/urandom
614
if os.path.exists("/dev/urandom"):
615
658
rand_bytes = file('/dev/urandom', 'rb').read
616
659
# Otherwise, use this hack as a last resort
660
except (IOError, OSError):
618
661
# not well seeded, but better than nothing
619
662
def rand_bytes(n):
642
685
## decomposition (might be too tricksy though.)
644
687
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)
688
"""Turn string into list of parts."""
689
assert isinstance(p, basestring)
662
691
# split on either delimiter because people might use either on
677
706
assert isinstance(p, list)
679
708
if (f == '..') or (f is None) or (f == ''):
680
raise BzrError("sorry, %r not allowed in path" % f)
709
raise errors.BzrError("sorry, %r not allowed in path" % f)
681
710
return pathjoin(*p)
705
734
def link_or_copy(src, dest):
706
735
"""Hardlink a file, or copy it if it can't be hardlinked."""
707
736
if not hardlinks_good():
737
shutil.copyfile(src, dest)
711
740
os.link(src, dest)
712
741
except (OSError, IOError), e:
713
742
if e.errno != errno.EXDEV:
744
shutil.copyfile(src, dest)
717
746
def delete_any(full_path):
718
747
"""Delete a file or directory."""
798
827
return unicode_or_utf8_string.decode('utf8')
799
828
except UnicodeDecodeError:
800
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
829
raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
803
832
_platform_normalizes_filenames = False
902
931
if sys.platform != "win32":
904
933
if _validWin32PathRE.match(path) is None:
905
raise IllegalPath(path)
934
raise errors.IllegalPath(path)
908
937
def walkdirs(top, prefix=""):