/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Alexander Belchenko
  • Date: 2007-02-14 10:09:00 UTC
  • mto: This revision was merged to the branch mainline in revision 2289.
  • Revision ID: bialix@ukr.net-20070214100900-i4rygckilzdjc1tz
Reimplementation of ntpath.abspath in Python for Windows98: unicode safe, UNC path safe

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
from cStringIO import StringIO
 
18
import os
 
19
import re
 
20
import stat
 
21
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
22
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
23
import sys
 
24
import time
 
25
 
 
26
from bzrlib.lazy_import import lazy_import
 
27
lazy_import(globals(), """
 
28
import codecs
 
29
from datetime import datetime
 
30
import errno
 
31
from ntpath import (abspath as _nt_abspath,
 
32
                    join as _nt_join,
 
33
                    normpath as _nt_normpath,
 
34
                    realpath as _nt_realpath,
 
35
                    splitdrive as _nt_splitdrive,
 
36
                    )
 
37
import posixpath
 
38
import sha
 
39
import shutil
 
40
from shutil import (
 
41
    rmtree,
 
42
    )
 
43
import tempfile
 
44
from tempfile import (
 
45
    mkdtemp,
 
46
    )
 
47
import unicodedata
 
48
 
 
49
from bzrlib import (
 
50
    errors,
 
51
    win32utils,
 
52
    )
 
53
""")
 
54
 
 
55
import bzrlib
 
56
from bzrlib.symbol_versioning import (
 
57
    deprecated_function,
 
58
    zero_nine,
 
59
    )
 
60
from bzrlib.trace import mutter
 
61
 
 
62
 
 
63
# On win32, O_BINARY is used to indicate the file should
 
64
# be opened in binary mode, rather than text mode.
 
65
# On other platforms, O_BINARY doesn't exist, because
 
66
# they always open in binary mode, so it is okay to
 
67
# OR with 0 on those platforms
 
68
O_BINARY = getattr(os, 'O_BINARY', 0)
 
69
 
 
70
 
 
71
def make_readonly(filename):
 
72
    """Make a filename read-only."""
 
73
    mod = os.stat(filename).st_mode
 
74
    mod = mod & 0777555
 
75
    os.chmod(filename, mod)
 
76
 
 
77
 
 
78
def make_writable(filename):
 
79
    mod = os.stat(filename).st_mode
 
80
    mod = mod | 0200
 
81
    os.chmod(filename, mod)
 
82
 
 
83
 
 
84
_QUOTE_RE = None
 
85
 
 
86
 
 
87
def quotefn(f):
 
88
    """Return a quoted filename filename
 
89
 
 
90
    This previously used backslash quoting, but that works poorly on
 
91
    Windows."""
 
92
    # TODO: I'm not really sure this is the best format either.x
 
93
    global _QUOTE_RE
 
94
    if _QUOTE_RE is None:
 
95
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
 
96
        
 
97
    if _QUOTE_RE.search(f):
 
98
        return '"' + f + '"'
 
99
    else:
 
100
        return f
 
101
 
 
102
 
 
103
_directory_kind = 'directory'
 
104
 
 
105
_formats = {
 
106
    stat.S_IFDIR:_directory_kind,
 
107
    stat.S_IFCHR:'chardev',
 
108
    stat.S_IFBLK:'block',
 
109
    stat.S_IFREG:'file',
 
110
    stat.S_IFIFO:'fifo',
 
111
    stat.S_IFLNK:'symlink',
 
112
    stat.S_IFSOCK:'socket',
 
113
}
 
114
 
 
115
 
 
116
def file_kind_from_stat_mode(stat_mode, _formats=_formats, _unknown='unknown'):
 
117
    """Generate a file kind from a stat mode. This is used in walkdirs.
 
118
 
 
119
    Its performance is critical: Do not mutate without careful benchmarking.
 
120
    """
 
121
    try:
 
122
        return _formats[stat_mode & 0170000]
 
123
    except KeyError:
 
124
        return _unknown
 
125
 
 
126
 
 
127
def file_kind(f, _lstat=os.lstat, _mapper=file_kind_from_stat_mode):
 
128
    try:
 
129
        return _mapper(_lstat(f).st_mode)
 
130
    except OSError, e:
 
131
        if getattr(e, 'errno', None) == errno.ENOENT:
 
132
            raise errors.NoSuchFile(f)
 
133
        raise
 
134
 
 
135
 
 
136
def get_umask():
 
137
    """Return the current umask"""
 
138
    # Assume that people aren't messing with the umask while running
 
139
    # XXX: This is not thread safe, but there is no way to get the
 
140
    #      umask without setting it
 
141
    umask = os.umask(0)
 
142
    os.umask(umask)
 
143
    return umask
 
144
 
 
145
 
 
146
def kind_marker(kind):
 
147
    if kind == 'file':
 
148
        return ''
 
149
    elif kind == _directory_kind:
 
150
        return '/'
 
151
    elif kind == 'symlink':
 
152
        return '@'
 
153
    else:
 
154
        raise errors.BzrError('invalid file kind %r' % kind)
 
155
 
 
156
lexists = getattr(os.path, 'lexists', None)
 
157
if lexists is None:
 
158
    def lexists(f):
 
159
        try:
 
160
            if getattr(os, 'lstat') is not None:
 
161
                os.lstat(f)
 
162
            else:
 
163
                os.stat(f)
 
164
            return True
 
165
        except OSError,e:
 
166
            if e.errno == errno.ENOENT:
 
167
                return False;
 
168
            else:
 
169
                raise errors.BzrError("lstat/stat of (%r): %r" % (f, e))
 
170
 
 
171
 
 
172
def fancy_rename(old, new, rename_func, unlink_func):
 
173
    """A fancy rename, when you don't have atomic rename.
 
174
    
 
175
    :param old: The old path, to rename from
 
176
    :param new: The new path, to rename to
 
177
    :param rename_func: The potentially non-atomic rename function
 
178
    :param unlink_func: A way to delete the target file if the full rename succeeds
 
179
    """
 
180
 
 
181
    # sftp rename doesn't allow overwriting, so play tricks:
 
182
    import random
 
183
    base = os.path.basename(new)
 
184
    dirname = os.path.dirname(new)
 
185
    tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
 
186
    tmp_name = pathjoin(dirname, tmp_name)
 
187
 
 
188
    # Rename the file out of the way, but keep track if it didn't exist
 
189
    # We don't want to grab just any exception
 
190
    # something like EACCES should prevent us from continuing
 
191
    # The downside is that the rename_func has to throw an exception
 
192
    # with an errno = ENOENT, or NoSuchFile
 
193
    file_existed = False
 
194
    try:
 
195
        rename_func(new, tmp_name)
 
196
    except (errors.NoSuchFile,), e:
 
197
        pass
 
198
    except IOError, e:
 
199
        # RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
 
200
        # function raises an IOError with errno is None when a rename fails.
 
201
        # This then gets caught here.
 
202
        if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
 
203
            raise
 
204
    except Exception, e:
 
205
        if (getattr(e, 'errno', None) is None
 
206
            or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
 
207
            raise
 
208
    else:
 
209
        file_existed = True
 
210
 
 
211
    success = False
 
212
    try:
 
213
        # This may throw an exception, in which case success will
 
214
        # not be set.
 
215
        rename_func(old, new)
 
216
        success = True
 
217
    finally:
 
218
        if file_existed:
 
219
            # If the file used to exist, rename it back into place
 
220
            # otherwise just delete it from the tmp location
 
221
            if success:
 
222
                unlink_func(tmp_name)
 
223
            else:
 
224
                rename_func(tmp_name, new)
 
225
 
 
226
 
 
227
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
 
228
# choke on a Unicode string containing a relative path if
 
229
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
 
230
# string.
 
231
_fs_enc = sys.getfilesystemencoding() or 'utf-8'
 
232
def _posix_abspath(path):
 
233
    # jam 20060426 rather than encoding to fsencoding
 
234
    # copy posixpath.abspath, but use os.getcwdu instead
 
235
    if not posixpath.isabs(path):
 
236
        path = posixpath.join(getcwd(), path)
 
237
    return posixpath.normpath(path)
 
238
 
 
239
 
 
240
def _posix_realpath(path):
 
241
    return posixpath.realpath(path.encode(_fs_enc)).decode(_fs_enc)
 
242
 
 
243
 
 
244
def _win32_fixdrive(path):
 
245
    """Force drive letters to be consistent.
 
246
 
 
247
    win32 is inconsistent whether it returns lower or upper case
 
248
    and even if it was consistent the user might type the other
 
249
    so we force it to uppercase
 
250
    running python.exe under cmd.exe return capital C:\\
 
251
    running win32 python inside a cygwin shell returns lowercase c:\\
 
252
    """
 
253
    drive, path = _nt_splitdrive(path)
 
254
    return drive.upper() + path
 
255
 
 
256
 
 
257
def _win32_abspath(path):
 
258
    # Real _nt_abspath doesn't have a problem with a unicode cwd
 
259
    return _win32_fixdrive(_nt_abspath(unicode(path)).replace('\\', '/'))
 
260
 
 
261
 
 
262
def _win98_abspath(path):
 
263
    """Return the absolute version of a path.
 
264
    Windows 98 safe implementation (python reimplementation
 
265
    of Win32 API function GetFullPathNameW)
 
266
    """
 
267
    # Corner cases:
 
268
    #   C:\path     => C:/path
 
269
    #   C:/path     => C:/path
 
270
    #   \\HOST\path => //HOST/path
 
271
    #   //HOST/path => //HOST/path
 
272
    #   path        => C:/cwd/path
 
273
    #   /path       => C:/path
 
274
    path = unicode(path)
 
275
    # check for absolute path
 
276
    drive = _nt_splitdrive(path)[0]
 
277
    if drive == '' and path[:2] not in('//','\\\\'):
 
278
        cwd = os.getcwdu()
 
279
        # we cannot simply os.path.join cwd and path
 
280
        # because os.path.join('C:','/path') produce '/path'
 
281
        # and this is incorrect
 
282
        if path[:1] in ('/','\\'):
 
283
            cwd = _nt_splitdrive(cwd)[0]
 
284
        path = cwd + '\\' + path
 
285
    return _win32_fixdrive(_nt_normpath(path).replace('\\', '/'))
 
286
 
 
287
if win32utils.winver == 'Windows 98':
 
288
    _win32_abspath = _win98_abspath
 
289
 
 
290
 
 
291
def _win32_realpath(path):
 
292
    # Real _nt_realpath doesn't have a problem with a unicode cwd
 
293
    return _win32_fixdrive(_nt_realpath(unicode(path)).replace('\\', '/'))
 
294
 
 
295
 
 
296
def _win32_pathjoin(*args):
 
297
    return _nt_join(*args).replace('\\', '/')
 
298
 
 
299
 
 
300
def _win32_normpath(path):
 
301
    return _win32_fixdrive(_nt_normpath(unicode(path)).replace('\\', '/'))
 
302
 
 
303
 
 
304
def _win32_getcwd():
 
305
    return _win32_fixdrive(os.getcwdu().replace('\\', '/'))
 
306
 
 
307
 
 
308
def _win32_mkdtemp(*args, **kwargs):
 
309
    return _win32_fixdrive(tempfile.mkdtemp(*args, **kwargs).replace('\\', '/'))
 
310
 
 
311
 
 
312
def _win32_rename(old, new):
 
313
    """We expect to be able to atomically replace 'new' with old.
 
314
 
 
315
    On win32, if new exists, it must be moved out of the way first,
 
316
    and then deleted. 
 
317
    """
 
318
    try:
 
319
        fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
 
320
    except OSError, e:
 
321
        if e.errno in (errno.EPERM, errno.EACCES, errno.EBUSY, errno.EINVAL):
 
322
            # If we try to rename a non-existant file onto cwd, we get 
 
323
            # EPERM or EACCES instead of ENOENT, this will raise ENOENT 
 
324
            # if the old path doesn't exist, sometimes we get EACCES
 
325
            # On Linux, we seem to get EBUSY, on Mac we get EINVAL
 
326
            os.lstat(old)
 
327
        raise
 
328
 
 
329
 
 
330
def _mac_getcwd():
 
331
    return unicodedata.normalize('NFKC', os.getcwdu())
 
332
 
 
333
 
 
334
# Default is to just use the python builtins, but these can be rebound on
 
335
# particular platforms.
 
336
abspath = _posix_abspath
 
337
realpath = _posix_realpath
 
338
pathjoin = os.path.join
 
339
normpath = os.path.normpath
 
340
getcwd = os.getcwdu
 
341
rename = os.rename
 
342
dirname = os.path.dirname
 
343
basename = os.path.basename
 
344
split = os.path.split
 
345
splitext = os.path.splitext
 
346
# These were already imported into local scope
 
347
# mkdtemp = tempfile.mkdtemp
 
348
# rmtree = shutil.rmtree
 
349
 
 
350
MIN_ABS_PATHLENGTH = 1
 
351
 
 
352
 
 
353
if sys.platform == 'win32':
 
354
    abspath = _win32_abspath
 
355
    realpath = _win32_realpath
 
356
    pathjoin = _win32_pathjoin
 
357
    normpath = _win32_normpath
 
358
    getcwd = _win32_getcwd
 
359
    mkdtemp = _win32_mkdtemp
 
360
    rename = _win32_rename
 
361
 
 
362
    MIN_ABS_PATHLENGTH = 3
 
363
 
 
364
    def _win32_delete_readonly(function, path, excinfo):
 
365
        """Error handler for shutil.rmtree function [for win32]
 
366
        Helps to remove files and dirs marked as read-only.
 
367
        """
 
368
        exception = excinfo[1]
 
369
        if function in (os.remove, os.rmdir) \
 
370
            and isinstance(exception, OSError) \
 
371
            and exception.errno == errno.EACCES:
 
372
            make_writable(path)
 
373
            function(path)
 
374
        else:
 
375
            raise
 
376
 
 
377
    def rmtree(path, ignore_errors=False, onerror=_win32_delete_readonly):
 
378
        """Replacer for shutil.rmtree: could remove readonly dirs/files"""
 
379
        return shutil.rmtree(path, ignore_errors, onerror)
 
380
elif sys.platform == 'darwin':
 
381
    getcwd = _mac_getcwd
 
382
 
 
383
 
 
384
def get_terminal_encoding():
 
385
    """Find the best encoding for printing to the screen.
 
386
 
 
387
    This attempts to check both sys.stdout and sys.stdin to see
 
388
    what encoding they are in, and if that fails it falls back to
 
389
    bzrlib.user_encoding.
 
390
    The problem is that on Windows, locale.getpreferredencoding()
 
391
    is not the same encoding as that used by the console:
 
392
    http://mail.python.org/pipermail/python-list/2003-May/162357.html
 
393
 
 
394
    On my standard US Windows XP, the preferred encoding is
 
395
    cp1252, but the console is cp437
 
396
    """
 
397
    output_encoding = getattr(sys.stdout, 'encoding', None)
 
398
    if not output_encoding:
 
399
        input_encoding = getattr(sys.stdin, 'encoding', None)
 
400
        if not input_encoding:
 
401
            output_encoding = bzrlib.user_encoding
 
402
            mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
403
        else:
 
404
            output_encoding = input_encoding
 
405
            mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
 
406
    else:
 
407
        mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
 
408
    if output_encoding == 'cp0':
 
409
        # invalid encoding (cp0 means 'no codepage' on Windows)
 
410
        output_encoding = bzrlib.user_encoding
 
411
        mutter('cp0 is invalid encoding.'
 
412
               ' encoding stdout as bzrlib.user_encoding %r', output_encoding)
 
413
    # check encoding
 
414
    try:
 
415
        codecs.lookup(output_encoding)
 
416
    except LookupError:
 
417
        sys.stderr.write('bzr: warning:'
 
418
                         ' unknown terminal encoding %s.\n'
 
419
                         '  Using encoding %s instead.\n'
 
420
                         % (output_encoding, bzrlib.user_encoding)
 
421
                        )
 
422
        output_encoding = bzrlib.user_encoding
 
423
 
 
424
    return output_encoding
 
425
 
 
426
 
 
427
def normalizepath(f):
 
428
    if getattr(os.path, 'realpath', None) is not None:
 
429
        F = realpath
 
430
    else:
 
431
        F = abspath
 
432
    [p,e] = os.path.split(f)
 
433
    if e == "" or e == "." or e == "..":
 
434
        return F(f)
 
435
    else:
 
436
        return pathjoin(F(p), e)
 
437
 
 
438
 
 
439
def backup_file(fn):
 
440
    """Copy a file to a backup.
 
441
 
 
442
    Backups are named in GNU-style, with a ~ suffix.
 
443
 
 
444
    If the file is already a backup, it's not copied.
 
445
    """
 
446
    if fn[-1] == '~':
 
447
        return
 
448
    bfn = fn + '~'
 
449
 
 
450
    if has_symlinks() and os.path.islink(fn):
 
451
        target = os.readlink(fn)
 
452
        os.symlink(target, bfn)
 
453
        return
 
454
    inf = file(fn, 'rb')
 
455
    try:
 
456
        content = inf.read()
 
457
    finally:
 
458
        inf.close()
 
459
    
 
460
    outf = file(bfn, 'wb')
 
461
    try:
 
462
        outf.write(content)
 
463
    finally:
 
464
        outf.close()
 
465
 
 
466
 
 
467
def isdir(f):
 
468
    """True if f is an accessible directory."""
 
469
    try:
 
470
        return S_ISDIR(os.lstat(f)[ST_MODE])
 
471
    except OSError:
 
472
        return False
 
473
 
 
474
 
 
475
def isfile(f):
 
476
    """True if f is a regular file."""
 
477
    try:
 
478
        return S_ISREG(os.lstat(f)[ST_MODE])
 
479
    except OSError:
 
480
        return False
 
481
 
 
482
def islink(f):
 
483
    """True if f is a symlink."""
 
484
    try:
 
485
        return S_ISLNK(os.lstat(f)[ST_MODE])
 
486
    except OSError:
 
487
        return False
 
488
 
 
489
def is_inside(dir, fname):
 
490
    """True if fname is inside dir.
 
491
    
 
492
    The parameters should typically be passed to osutils.normpath first, so
 
493
    that . and .. and repeated slashes are eliminated, and the separators
 
494
    are canonical for the platform.
 
495
    
 
496
    The empty string as a dir name is taken as top-of-tree and matches 
 
497
    everything.
 
498
    """
 
499
    # XXX: Most callers of this can actually do something smarter by 
 
500
    # looking at the inventory
 
501
    if dir == fname:
 
502
        return True
 
503
    
 
504
    if dir == '':
 
505
        return True
 
506
 
 
507
    if dir[-1] != '/':
 
508
        dir += '/'
 
509
 
 
510
    return fname.startswith(dir)
 
511
 
 
512
 
 
513
def is_inside_any(dir_list, fname):
 
514
    """True if fname is inside any of given dirs."""
 
515
    for dirname in dir_list:
 
516
        if is_inside(dirname, fname):
 
517
            return True
 
518
    else:
 
519
        return False
 
520
 
 
521
 
 
522
def is_inside_or_parent_of_any(dir_list, fname):
 
523
    """True if fname is a child or a parent of any of the given files."""
 
524
    for dirname in dir_list:
 
525
        if is_inside(dirname, fname) or is_inside(fname, dirname):
 
526
            return True
 
527
    else:
 
528
        return False
 
529
 
 
530
 
 
531
def pumpfile(fromfile, tofile):
 
532
    """Copy contents of one file to another."""
 
533
    BUFSIZE = 32768
 
534
    while True:
 
535
        b = fromfile.read(BUFSIZE)
 
536
        if not b:
 
537
            break
 
538
        tofile.write(b)
 
539
 
 
540
 
 
541
def file_iterator(input_file, readsize=32768):
 
542
    while True:
 
543
        b = input_file.read(readsize)
 
544
        if len(b) == 0:
 
545
            break
 
546
        yield b
 
547
 
 
548
 
 
549
def sha_file(f):
 
550
    if getattr(f, 'tell', None) is not None:
 
551
        assert f.tell() == 0
 
552
    s = sha.new()
 
553
    BUFSIZE = 128<<10
 
554
    while True:
 
555
        b = f.read(BUFSIZE)
 
556
        if not b:
 
557
            break
 
558
        s.update(b)
 
559
    return s.hexdigest()
 
560
 
 
561
 
 
562
 
 
563
def sha_strings(strings):
 
564
    """Return the sha-1 of concatenation of strings"""
 
565
    s = sha.new()
 
566
    map(s.update, strings)
 
567
    return s.hexdigest()
 
568
 
 
569
 
 
570
def sha_string(f):
 
571
    s = sha.new()
 
572
    s.update(f)
 
573
    return s.hexdigest()
 
574
 
 
575
 
 
576
def fingerprint_file(f):
 
577
    s = sha.new()
 
578
    b = f.read()
 
579
    s.update(b)
 
580
    size = len(b)
 
581
    return {'size': size,
 
582
            'sha1': s.hexdigest()}
 
583
 
 
584
 
 
585
def compare_files(a, b):
 
586
    """Returns true if equal in contents"""
 
587
    BUFSIZE = 4096
 
588
    while True:
 
589
        ai = a.read(BUFSIZE)
 
590
        bi = b.read(BUFSIZE)
 
591
        if ai != bi:
 
592
            return False
 
593
        if ai == '':
 
594
            return True
 
595
 
 
596
 
 
597
def local_time_offset(t=None):
 
598
    """Return offset of local zone from GMT, either at present or at time t."""
 
599
    if t is None:
 
600
        t = time.time()
 
601
    offset = datetime.fromtimestamp(t) - datetime.utcfromtimestamp(t)
 
602
    return offset.days * 86400 + offset.seconds
 
603
 
 
604
    
 
605
def format_date(t, offset=0, timezone='original', date_fmt=None, 
 
606
                show_offset=True):
 
607
    ## TODO: Perhaps a global option to use either universal or local time?
 
608
    ## Or perhaps just let people set $TZ?
 
609
    assert isinstance(t, float)
 
610
    
 
611
    if timezone == 'utc':
 
612
        tt = time.gmtime(t)
 
613
        offset = 0
 
614
    elif timezone == 'original':
 
615
        if offset is None:
 
616
            offset = 0
 
617
        tt = time.gmtime(t + offset)
 
618
    elif timezone == 'local':
 
619
        tt = time.localtime(t)
 
620
        offset = local_time_offset(t)
 
621
    else:
 
622
        raise errors.BzrError("unsupported timezone format %r" % timezone,
 
623
                              ['options are "utc", "original", "local"'])
 
624
    if date_fmt is None:
 
625
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
 
626
    if show_offset:
 
627
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
 
628
    else:
 
629
        offset_str = ''
 
630
    return (time.strftime(date_fmt, tt) +  offset_str)
 
631
 
 
632
 
 
633
def compact_date(when):
 
634
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
 
635
    
 
636
 
 
637
def format_delta(delta):
 
638
    """Get a nice looking string for a time delta.
 
639
 
 
640
    :param delta: The time difference in seconds, can be positive or negative.
 
641
        positive indicates time in the past, negative indicates time in the
 
642
        future. (usually time.time() - stored_time)
 
643
    :return: String formatted to show approximate resolution
 
644
    """
 
645
    delta = int(delta)
 
646
    if delta >= 0:
 
647
        direction = 'ago'
 
648
    else:
 
649
        direction = 'in the future'
 
650
        delta = -delta
 
651
 
 
652
    seconds = delta
 
653
    if seconds < 90: # print seconds up to 90 seconds
 
654
        if seconds == 1:
 
655
            return '%d second %s' % (seconds, direction,)
 
656
        else:
 
657
            return '%d seconds %s' % (seconds, direction)
 
658
 
 
659
    minutes = int(seconds / 60)
 
660
    seconds -= 60 * minutes
 
661
    if seconds == 1:
 
662
        plural_seconds = ''
 
663
    else:
 
664
        plural_seconds = 's'
 
665
    if minutes < 90: # print minutes, seconds up to 90 minutes
 
666
        if minutes == 1:
 
667
            return '%d minute, %d second%s %s' % (
 
668
                    minutes, seconds, plural_seconds, direction)
 
669
        else:
 
670
            return '%d minutes, %d second%s %s' % (
 
671
                    minutes, seconds, plural_seconds, direction)
 
672
 
 
673
    hours = int(minutes / 60)
 
674
    minutes -= 60 * hours
 
675
    if minutes == 1:
 
676
        plural_minutes = ''
 
677
    else:
 
678
        plural_minutes = 's'
 
679
 
 
680
    if hours == 1:
 
681
        return '%d hour, %d minute%s %s' % (hours, minutes,
 
682
                                            plural_minutes, direction)
 
683
    return '%d hours, %d minute%s %s' % (hours, minutes,
 
684
                                         plural_minutes, direction)
 
685
 
 
686
def filesize(f):
 
687
    """Return size of given open file."""
 
688
    return os.fstat(f.fileno())[ST_SIZE]
 
689
 
 
690
 
 
691
# Define rand_bytes based on platform.
 
692
try:
 
693
    # Python 2.4 and later have os.urandom,
 
694
    # but it doesn't work on some arches
 
695
    os.urandom(1)
 
696
    rand_bytes = os.urandom
 
697
except (NotImplementedError, AttributeError):
 
698
    # If python doesn't have os.urandom, or it doesn't work,
 
699
    # then try to first pull random data from /dev/urandom
 
700
    try:
 
701
        rand_bytes = file('/dev/urandom', 'rb').read
 
702
    # Otherwise, use this hack as a last resort
 
703
    except (IOError, OSError):
 
704
        # not well seeded, but better than nothing
 
705
        def rand_bytes(n):
 
706
            import random
 
707
            s = ''
 
708
            while n:
 
709
                s += chr(random.randint(0, 255))
 
710
                n -= 1
 
711
            return s
 
712
 
 
713
 
 
714
ALNUM = '0123456789abcdefghijklmnopqrstuvwxyz'
 
715
def rand_chars(num):
 
716
    """Return a random string of num alphanumeric characters
 
717
    
 
718
    The result only contains lowercase chars because it may be used on 
 
719
    case-insensitive filesystems.
 
720
    """
 
721
    s = ''
 
722
    for raw_byte in rand_bytes(num):
 
723
        s += ALNUM[ord(raw_byte) % 36]
 
724
    return s
 
725
 
 
726
 
 
727
## TODO: We could later have path objects that remember their list
 
728
## decomposition (might be too tricksy though.)
 
729
 
 
730
def splitpath(p):
 
731
    """Turn string into list of parts."""
 
732
    assert isinstance(p, basestring)
 
733
 
 
734
    # split on either delimiter because people might use either on
 
735
    # Windows
 
736
    ps = re.split(r'[\\/]', p)
 
737
 
 
738
    rps = []
 
739
    for f in ps:
 
740
        if f == '..':
 
741
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
742
        elif (f == '.') or (f == ''):
 
743
            pass
 
744
        else:
 
745
            rps.append(f)
 
746
    return rps
 
747
 
 
748
def joinpath(p):
 
749
    assert isinstance(p, list)
 
750
    for f in p:
 
751
        if (f == '..') or (f is None) or (f == ''):
 
752
            raise errors.BzrError("sorry, %r not allowed in path" % f)
 
753
    return pathjoin(*p)
 
754
 
 
755
 
 
756
@deprecated_function(zero_nine)
 
757
def appendpath(p1, p2):
 
758
    if p1 == '':
 
759
        return p2
 
760
    else:
 
761
        return pathjoin(p1, p2)
 
762
    
 
763
 
 
764
def split_lines(s):
 
765
    """Split s into lines, but without removing the newline characters."""
 
766
    lines = s.split('\n')
 
767
    result = [line + '\n' for line in lines[:-1]]
 
768
    if lines[-1]:
 
769
        result.append(lines[-1])
 
770
    return result
 
771
 
 
772
 
 
773
def hardlinks_good():
 
774
    return sys.platform not in ('win32', 'cygwin', 'darwin')
 
775
 
 
776
 
 
777
def link_or_copy(src, dest):
 
778
    """Hardlink a file, or copy it if it can't be hardlinked."""
 
779
    if not hardlinks_good():
 
780
        shutil.copyfile(src, dest)
 
781
        return
 
782
    try:
 
783
        os.link(src, dest)
 
784
    except (OSError, IOError), e:
 
785
        if e.errno != errno.EXDEV:
 
786
            raise
 
787
        shutil.copyfile(src, dest)
 
788
 
 
789
def delete_any(full_path):
 
790
    """Delete a file or directory."""
 
791
    try:
 
792
        os.unlink(full_path)
 
793
    except OSError, e:
 
794
    # We may be renaming a dangling inventory id
 
795
        if e.errno not in (errno.EISDIR, errno.EACCES, errno.EPERM):
 
796
            raise
 
797
        os.rmdir(full_path)
 
798
 
 
799
 
 
800
def has_symlinks():
 
801
    if getattr(os, 'symlink', None) is not None:
 
802
        return True
 
803
    else:
 
804
        return False
 
805
        
 
806
 
 
807
def contains_whitespace(s):
 
808
    """True if there are any whitespace characters in s."""
 
809
    # string.whitespace can include '\xa0' in certain locales, because it is
 
810
    # considered "non-breaking-space" as part of ISO-8859-1. But it
 
811
    # 1) Isn't a breaking whitespace
 
812
    # 2) Isn't one of ' \t\r\n' which are characters we sometimes use as
 
813
    #    separators
 
814
    # 3) '\xa0' isn't unicode safe since it is >128.
 
815
    # So we are following textwrap's example and hard-coding our own.
 
816
    # We probably could ignore \v and \f, too.
 
817
    for ch in u' \t\n\r\v\f':
 
818
        if ch in s:
 
819
            return True
 
820
    else:
 
821
        return False
 
822
 
 
823
 
 
824
def contains_linebreaks(s):
 
825
    """True if there is any vertical whitespace in s."""
 
826
    for ch in '\f\n\r':
 
827
        if ch in s:
 
828
            return True
 
829
    else:
 
830
        return False
 
831
 
 
832
 
 
833
def relpath(base, path):
 
834
    """Return path relative to base, or raise exception.
 
835
 
 
836
    The path may be either an absolute path or a path relative to the
 
837
    current working directory.
 
838
 
 
839
    os.path.commonprefix (python2.4) has a bad bug that it works just
 
840
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
 
841
    avoids that problem.
 
842
    """
 
843
 
 
844
    assert len(base) >= MIN_ABS_PATHLENGTH, ('Length of base must be equal or'
 
845
        ' exceed the platform minimum length (which is %d)' % 
 
846
        MIN_ABS_PATHLENGTH)
 
847
 
 
848
    rp = abspath(path)
 
849
 
 
850
    s = []
 
851
    head = rp
 
852
    while len(head) >= len(base):
 
853
        if head == base:
 
854
            break
 
855
        head, tail = os.path.split(head)
 
856
        if tail:
 
857
            s.insert(0, tail)
 
858
    else:
 
859
        raise errors.PathNotChild(rp, base)
 
860
 
 
861
    if s:
 
862
        return pathjoin(*s)
 
863
    else:
 
864
        return ''
 
865
 
 
866
 
 
867
def safe_unicode(unicode_or_utf8_string):
 
868
    """Coerce unicode_or_utf8_string into unicode.
 
869
 
 
870
    If it is unicode, it is returned.
 
871
    Otherwise it is decoded from utf-8. If a decoding error
 
872
    occurs, it is wrapped as a If the decoding fails, the exception is wrapped 
 
873
    as a BzrBadParameter exception.
 
874
    """
 
875
    if isinstance(unicode_or_utf8_string, unicode):
 
876
        return unicode_or_utf8_string
 
877
    try:
 
878
        return unicode_or_utf8_string.decode('utf8')
 
879
    except UnicodeDecodeError:
 
880
        raise errors.BzrBadParameterNotUnicode(unicode_or_utf8_string)
 
881
 
 
882
 
 
883
_platform_normalizes_filenames = False
 
884
if sys.platform == 'darwin':
 
885
    _platform_normalizes_filenames = True
 
886
 
 
887
 
 
888
def normalizes_filenames():
 
889
    """Return True if this platform normalizes unicode filenames.
 
890
 
 
891
    Mac OSX does, Windows/Linux do not.
 
892
    """
 
893
    return _platform_normalizes_filenames
 
894
 
 
895
 
 
896
def _accessible_normalized_filename(path):
 
897
    """Get the unicode normalized path, and if you can access the file.
 
898
 
 
899
    On platforms where the system normalizes filenames (Mac OSX),
 
900
    you can access a file by any path which will normalize correctly.
 
901
    On platforms where the system does not normalize filenames 
 
902
    (Windows, Linux), you have to access a file by its exact path.
 
903
 
 
904
    Internally, bzr only supports NFC/NFKC normalization, since that is 
 
905
    the standard for XML documents.
 
906
 
 
907
    So return the normalized path, and a flag indicating if the file
 
908
    can be accessed by that path.
 
909
    """
 
910
 
 
911
    return unicodedata.normalize('NFKC', unicode(path)), True
 
912
 
 
913
 
 
914
def _inaccessible_normalized_filename(path):
 
915
    __doc__ = _accessible_normalized_filename.__doc__
 
916
 
 
917
    normalized = unicodedata.normalize('NFKC', unicode(path))
 
918
    return normalized, normalized == path
 
919
 
 
920
 
 
921
if _platform_normalizes_filenames:
 
922
    normalized_filename = _accessible_normalized_filename
 
923
else:
 
924
    normalized_filename = _inaccessible_normalized_filename
 
925
 
 
926
 
 
927
def terminal_width():
 
928
    """Return estimated terminal width."""
 
929
    if sys.platform == 'win32':
 
930
        return win32utils.get_console_size()[0]
 
931
    width = 0
 
932
    try:
 
933
        import struct, fcntl, termios
 
934
        s = struct.pack('HHHH', 0, 0, 0, 0)
 
935
        x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
 
936
        width = struct.unpack('HHHH', x)[1]
 
937
    except IOError:
 
938
        pass
 
939
    if width <= 0:
 
940
        try:
 
941
            width = int(os.environ['COLUMNS'])
 
942
        except:
 
943
            pass
 
944
    if width <= 0:
 
945
        width = 80
 
946
 
 
947
    return width
 
948
 
 
949
 
 
950
def supports_executable():
 
951
    return sys.platform != "win32"
 
952
 
 
953
 
 
954
def supports_posix_readonly():
 
955
    """Return True if 'readonly' has POSIX semantics, False otherwise.
 
956
 
 
957
    Notably, a win32 readonly file cannot be deleted, unlike POSIX where the
 
958
    directory controls creation/deletion, etc.
 
959
 
 
960
    And under win32, readonly means that the directory itself cannot be
 
961
    deleted.  The contents of a readonly directory can be changed, unlike POSIX
 
962
    where files in readonly directories cannot be added, deleted or renamed.
 
963
    """
 
964
    return sys.platform != "win32"
 
965
 
 
966
 
 
967
def set_or_unset_env(env_variable, value):
 
968
    """Modify the environment, setting or removing the env_variable.
 
969
 
 
970
    :param env_variable: The environment variable in question
 
971
    :param value: The value to set the environment to. If None, then
 
972
        the variable will be removed.
 
973
    :return: The original value of the environment variable.
 
974
    """
 
975
    orig_val = os.environ.get(env_variable)
 
976
    if value is None:
 
977
        if orig_val is not None:
 
978
            del os.environ[env_variable]
 
979
    else:
 
980
        if isinstance(value, unicode):
 
981
            value = value.encode(bzrlib.user_encoding)
 
982
        os.environ[env_variable] = value
 
983
    return orig_val
 
984
 
 
985
 
 
986
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
 
987
 
 
988
 
 
989
def check_legal_path(path):
 
990
    """Check whether the supplied path is legal.  
 
991
    This is only required on Windows, so we don't test on other platforms
 
992
    right now.
 
993
    """
 
994
    if sys.platform != "win32":
 
995
        return
 
996
    if _validWin32PathRE.match(path) is None:
 
997
        raise errors.IllegalPath(path)
 
998
 
 
999
 
 
1000
def walkdirs(top, prefix=""):
 
1001
    """Yield data about all the directories in a tree.
 
1002
    
 
1003
    This yields all the data about the contents of a directory at a time.
 
1004
    After each directory has been yielded, if the caller has mutated the list
 
1005
    to exclude some directories, they are then not descended into.
 
1006
    
 
1007
    The data yielded is of the form:
 
1008
    ((directory-relpath, directory-path-from-top),
 
1009
    [(relpath, basename, kind, lstat), ...]),
 
1010
     - directory-relpath is the relative path of the directory being returned
 
1011
       with respect to top. prefix is prepended to this.
 
1012
     - directory-path-from-root is the path including top for this directory. 
 
1013
       It is suitable for use with os functions.
 
1014
     - relpath is the relative path within the subtree being walked.
 
1015
     - basename is the basename of the path
 
1016
     - kind is the kind of the file now. If unknown then the file is not
 
1017
       present within the tree - but it may be recorded as versioned. See
 
1018
       versioned_kind.
 
1019
     - lstat is the stat data *if* the file was statted.
 
1020
     - planned, not implemented: 
 
1021
       path_from_tree_root is the path from the root of the tree.
 
1022
 
 
1023
    :param prefix: Prefix the relpaths that are yielded with 'prefix'. This 
 
1024
        allows one to walk a subtree but get paths that are relative to a tree
 
1025
        rooted higher up.
 
1026
    :return: an iterator over the dirs.
 
1027
    """
 
1028
    #TODO there is a bit of a smell where the results of the directory-
 
1029
    # summary in this, and the path from the root, may not agree 
 
1030
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
 
1031
    # potentially confusing output. We should make this more robust - but
 
1032
    # not at a speed cost. RBC 20060731
 
1033
    lstat = os.lstat
 
1034
    pending = []
 
1035
    _directory = _directory_kind
 
1036
    _listdir = os.listdir
 
1037
    pending = [(prefix, "", _directory, None, top)]
 
1038
    while pending:
 
1039
        dirblock = []
 
1040
        currentdir = pending.pop()
 
1041
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
 
1042
        top = currentdir[4]
 
1043
        if currentdir[0]:
 
1044
            relroot = currentdir[0] + '/'
 
1045
        else:
 
1046
            relroot = ""
 
1047
        for name in sorted(_listdir(top)):
 
1048
            abspath = top + '/' + name
 
1049
            statvalue = lstat(abspath)
 
1050
            dirblock.append((relroot + name, name,
 
1051
                file_kind_from_stat_mode(statvalue.st_mode),
 
1052
                statvalue, abspath))
 
1053
        yield (currentdir[0], top), dirblock
 
1054
        # push the user specified dirs from dirblock
 
1055
        for dir in reversed(dirblock):
 
1056
            if dir[2] == _directory:
 
1057
                pending.append(dir)
 
1058
 
 
1059
 
 
1060
def copy_tree(from_path, to_path, handlers={}):
 
1061
    """Copy all of the entries in from_path into to_path.
 
1062
 
 
1063
    :param from_path: The base directory to copy. 
 
1064
    :param to_path: The target directory. If it does not exist, it will
 
1065
        be created.
 
1066
    :param handlers: A dictionary of functions, which takes a source and
 
1067
        destinations for files, directories, etc.
 
1068
        It is keyed on the file kind, such as 'directory', 'symlink', or 'file'
 
1069
        'file', 'directory', and 'symlink' should always exist.
 
1070
        If they are missing, they will be replaced with 'os.mkdir()',
 
1071
        'os.readlink() + os.symlink()', and 'shutil.copy2()', respectively.
 
1072
    """
 
1073
    # Now, just copy the existing cached tree to the new location
 
1074
    # We use a cheap trick here.
 
1075
    # Absolute paths are prefixed with the first parameter
 
1076
    # relative paths are prefixed with the second.
 
1077
    # So we can get both the source and target returned
 
1078
    # without any extra work.
 
1079
 
 
1080
    def copy_dir(source, dest):
 
1081
        os.mkdir(dest)
 
1082
 
 
1083
    def copy_link(source, dest):
 
1084
        """Copy the contents of a symlink"""
 
1085
        link_to = os.readlink(source)
 
1086
        os.symlink(link_to, dest)
 
1087
 
 
1088
    real_handlers = {'file':shutil.copy2,
 
1089
                     'symlink':copy_link,
 
1090
                     'directory':copy_dir,
 
1091
                    }
 
1092
    real_handlers.update(handlers)
 
1093
 
 
1094
    if not os.path.exists(to_path):
 
1095
        real_handlers['directory'](from_path, to_path)
 
1096
 
 
1097
    for dir_info, entries in walkdirs(from_path, prefix=to_path):
 
1098
        for relpath, name, kind, st, abspath in entries:
 
1099
            real_handlers[kind](abspath, relpath)
 
1100
 
 
1101
 
 
1102
def path_prefix_key(path):
 
1103
    """Generate a prefix-order path key for path.
 
1104
 
 
1105
    This can be used to sort paths in the same way that walkdirs does.
 
1106
    """
 
1107
    return (dirname(path) , path)
 
1108
 
 
1109
 
 
1110
def compare_paths_prefix_order(path_a, path_b):
 
1111
    """Compare path_a and path_b to generate the same order walkdirs uses."""
 
1112
    key_a = path_prefix_key(path_a)
 
1113
    key_b = path_prefix_key(path_b)
 
1114
    return cmp(key_a, key_b)
 
1115
 
 
1116
 
 
1117
_cached_user_encoding = None
 
1118
 
 
1119
 
 
1120
def get_user_encoding(use_cache=True):
 
1121
    """Find out what the preferred user encoding is.
 
1122
 
 
1123
    This is generally the encoding that is used for command line parameters
 
1124
    and file contents. This may be different from the terminal encoding
 
1125
    or the filesystem encoding.
 
1126
 
 
1127
    :param  use_cache:  Enable cache for detected encoding.
 
1128
                        (This parameter is turned on by default,
 
1129
                        and required only for selftesting)
 
1130
 
 
1131
    :return: A string defining the preferred user encoding
 
1132
    """
 
1133
    global _cached_user_encoding
 
1134
    if _cached_user_encoding is not None and use_cache:
 
1135
        return _cached_user_encoding
 
1136
 
 
1137
    if sys.platform == 'darwin':
 
1138
        # work around egregious python 2.4 bug
 
1139
        sys.platform = 'posix'
 
1140
        try:
 
1141
            import locale
 
1142
        finally:
 
1143
            sys.platform = 'darwin'
 
1144
    else:
 
1145
        import locale
 
1146
 
 
1147
    try:
 
1148
        user_encoding = locale.getpreferredencoding()
 
1149
    except locale.Error, e:
 
1150
        sys.stderr.write('bzr: warning: %s\n'
 
1151
                         '  Could not determine what text encoding to use.\n'
 
1152
                         '  This error usually means your Python interpreter\n'
 
1153
                         '  doesn\'t support the locale set by $LANG (%s)\n'
 
1154
                         "  Continuing with ascii encoding.\n"
 
1155
                         % (e, os.environ.get('LANG')))
 
1156
        user_encoding = 'ascii'
 
1157
 
 
1158
    # Windows returns 'cp0' to indicate there is no code page. So we'll just
 
1159
    # treat that as ASCII, and not support printing unicode characters to the
 
1160
    # console.
 
1161
    if user_encoding in (None, 'cp0'):
 
1162
        user_encoding = 'ascii'
 
1163
    else:
 
1164
        # check encoding
 
1165
        try:
 
1166
            codecs.lookup(user_encoding)
 
1167
        except LookupError:
 
1168
            sys.stderr.write('bzr: warning:'
 
1169
                             ' unknown encoding %s.'
 
1170
                             ' Continuing with ascii encoding.\n'
 
1171
                             % user_encoding
 
1172
                            )
 
1173
            user_encoding = 'ascii'
 
1174
 
 
1175
    if use_cache:
 
1176
        _cached_user_encoding = user_encoding
 
1177
 
 
1178
    return user_encoding
 
1179
 
 
1180
 
 
1181
def recv_all(socket, bytes):
 
1182
    """Receive an exact number of bytes.
 
1183
 
 
1184
    Regular Socket.recv() may return less than the requested number of bytes,
 
1185
    dependning on what's in the OS buffer.  MSG_WAITALL is not available
 
1186
    on all platforms, but this should work everywhere.  This will return
 
1187
    less than the requested amount if the remote end closes.
 
1188
 
 
1189
    This isn't optimized and is intended mostly for use in testing.
 
1190
    """
 
1191
    b = ''
 
1192
    while len(b) < bytes:
 
1193
        new = socket.recv(bytes - len(b))
 
1194
        if new == '':
 
1195
            break # eof
 
1196
        b += new
 
1197
    return b
 
1198
 
 
1199
def dereference_path(path):
 
1200
    """Determine the real path to a file.
 
1201
 
 
1202
    All parent elements are dereferenced.  But the file itself is not
 
1203
    dereferenced.
 
1204
    :param path: The original path.  May be absolute or relative.
 
1205
    :return: the real path *to* the file
 
1206
    """
 
1207
    parent, base = os.path.split(path)
 
1208
    # The pathjoin for '.' is a workaround for Python bug #1213894.
 
1209
    # (initial path components aren't dereferenced)
 
1210
    return pathjoin(realpath(pathjoin('.', parent)), base)