/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: John Arbash Meinel
  • Date: 2005-12-01 19:57:43 UTC
  • mto: (1185.50.19 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1532.
  • Revision ID: john@arbash-meinel.com-20051201195743-57aefc694d237938
Reintroduced ensure_config_dir_exists() for sftp

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Bazaar-NG -- distributed version control
 
2
#
 
3
# Copyright (C) 2005 by Canonical Ltd
 
4
#
 
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.
 
9
#
 
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.
 
14
#
 
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
 
18
 
 
19
from shutil import copyfile
 
20
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
21
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
22
from cStringIO import StringIO
 
23
import errno
 
24
import os
 
25
import re
 
26
import sha
 
27
import string
 
28
import sys
 
29
import time
 
30
import types
 
31
import tempfile
 
32
 
 
33
import bzrlib
 
34
from bzrlib.errors import BzrError, PathNotChild
 
35
from bzrlib.trace import mutter
 
36
 
 
37
 
 
38
def make_readonly(filename):
 
39
    """Make a filename read-only."""
 
40
    mod = os.stat(filename).st_mode
 
41
    mod = mod & 0777555
 
42
    os.chmod(filename, mod)
 
43
 
 
44
 
 
45
def make_writable(filename):
 
46
    mod = os.stat(filename).st_mode
 
47
    mod = mod | 0200
 
48
    os.chmod(filename, mod)
 
49
 
 
50
 
 
51
_QUOTE_RE = None
 
52
 
 
53
 
 
54
def quotefn(f):
 
55
    """Return a quoted filename filename
 
56
 
 
57
    This previously used backslash quoting, but that works poorly on
 
58
    Windows."""
 
59
    # TODO: I'm not really sure this is the best format either.x
 
60
    global _QUOTE_RE
 
61
    if _QUOTE_RE == None:
 
62
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
 
63
        
 
64
    if _QUOTE_RE.search(f):
 
65
        return '"' + f + '"'
 
66
    else:
 
67
        return f
 
68
 
 
69
 
 
70
def file_kind(f):
 
71
    mode = os.lstat(f)[ST_MODE]
 
72
    if S_ISREG(mode):
 
73
        return 'file'
 
74
    elif S_ISDIR(mode):
 
75
        return 'directory'
 
76
    elif S_ISLNK(mode):
 
77
        return 'symlink'
 
78
    elif S_ISCHR(mode):
 
79
        return 'chardev'
 
80
    elif S_ISBLK(mode):
 
81
        return 'block'
 
82
    elif S_ISFIFO(mode):
 
83
        return 'fifo'
 
84
    elif S_ISSOCK(mode):
 
85
        return 'socket'
 
86
    else:
 
87
        return 'unknown'
 
88
 
 
89
 
 
90
def kind_marker(kind):
 
91
    if kind == 'file':
 
92
        return ''
 
93
    elif kind == 'directory':
 
94
        return '/'
 
95
    elif kind == 'symlink':
 
96
        return '@'
 
97
    else:
 
98
        raise BzrError('invalid file kind %r' % kind)
 
99
 
 
100
def lexists(f):
 
101
    if hasattr(os.path, 'lexists'):
 
102
        return os.path.lexists(f)
 
103
    try:
 
104
        if hasattr(os, 'lstat'):
 
105
            os.lstat(f)
 
106
        else:
 
107
            os.stat(f)
 
108
        return True
 
109
    except OSError,e:
 
110
        if e.errno == errno.ENOENT:
 
111
            return False;
 
112
        else:
 
113
            raise BzrError("lstat/stat of (%r): %r" % (f, e))
 
114
 
 
115
if os.name == "posix":
 
116
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
 
117
    # choke on a Unicode string containing a relative path if
 
118
    # os.getcwd() returns a non-sys.getdefaultencoding()-encoded
 
119
    # string.
 
120
    _fs_enc = sys.getfilesystemencoding()
 
121
    def abspath(path):
 
122
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
 
123
    def realpath(path):
 
124
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
 
125
    pathjoin = os.path.join
 
126
    normpath = os.path.normpath
 
127
    getcwd = os.getcwdu
 
128
    mkdtemp = tempfile.mkdtemp
 
129
else:
 
130
    # We need to use the Unicode-aware os.path.abspath and
 
131
    # os.path.realpath on Windows systems.
 
132
    def abspath(path):
 
133
        return os.path.abspath(path).replace('\\', '/')
 
134
    def realpath(path):
 
135
        return os.path.realpath(path).replace('\\', '/')
 
136
    def pathjoin(*args):
 
137
        return os.path.join(*args).replace('\\', '/')
 
138
    def normpath(path):
 
139
        return os.path.normpath(path).replace('\\', '/')
 
140
    def getcwd():
 
141
        return os.getcwdu().replace('\\', '/')
 
142
    def mkdtemp(*args, **kwargs):
 
143
        return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
 
144
# Because these shrink the path, we can use the original
 
145
# versions on any platform
 
146
dirname = os.path.dirname
 
147
basename = os.path.basename
 
148
 
 
149
def normalizepath(f):
 
150
    if hasattr(os.path, 'realpath'):
 
151
        F = realpath
 
152
    else:
 
153
        F = abspath
 
154
    [p,e] = os.path.split(f)
 
155
    if e == "" or e == "." or e == "..":
 
156
        return F(f)
 
157
    else:
 
158
        return pathjoin(F(p), e)
 
159
 
 
160
 
 
161
def backup_file(fn):
 
162
    """Copy a file to a backup.
 
163
 
 
164
    Backups are named in GNU-style, with a ~ suffix.
 
165
 
 
166
    If the file is already a backup, it's not copied.
 
167
    """
 
168
    if fn[-1] == '~':
 
169
        return
 
170
    bfn = fn + '~'
 
171
 
 
172
    if has_symlinks() and os.path.islink(fn):
 
173
        target = os.readlink(fn)
 
174
        os.symlink(target, bfn)
 
175
        return
 
176
    inf = file(fn, 'rb')
 
177
    try:
 
178
        content = inf.read()
 
179
    finally:
 
180
        inf.close()
 
181
    
 
182
    outf = file(bfn, 'wb')
 
183
    try:
 
184
        outf.write(content)
 
185
    finally:
 
186
        outf.close()
 
187
 
 
188
if os.name == 'nt':
 
189
    import shutil
 
190
    rename = shutil.move
 
191
else:
 
192
    rename = os.rename
 
193
 
 
194
 
 
195
def isdir(f):
 
196
    """True if f is an accessible directory."""
 
197
    try:
 
198
        return S_ISDIR(os.lstat(f)[ST_MODE])
 
199
    except OSError:
 
200
        return False
 
201
 
 
202
 
 
203
def isfile(f):
 
204
    """True if f is a regular file."""
 
205
    try:
 
206
        return S_ISREG(os.lstat(f)[ST_MODE])
 
207
    except OSError:
 
208
        return False
 
209
 
 
210
def islink(f):
 
211
    """True if f is a symlink."""
 
212
    try:
 
213
        return S_ISLNK(os.lstat(f)[ST_MODE])
 
214
    except OSError:
 
215
        return False
 
216
 
 
217
def is_inside(dir, fname):
 
218
    """True if fname is inside dir.
 
219
    
 
220
    The parameters should typically be passed to osutils.normpath first, so
 
221
    that . and .. and repeated slashes are eliminated, and the separators
 
222
    are canonical for the platform.
 
223
    
 
224
    The empty string as a dir name is taken as top-of-tree and matches 
 
225
    everything.
 
226
    
 
227
    >>> is_inside('src', pathjoin('src', 'foo.c'))
 
228
    True
 
229
    >>> is_inside('src', 'srccontrol')
 
230
    False
 
231
    >>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
 
232
    True
 
233
    >>> is_inside('foo.c', 'foo.c')
 
234
    True
 
235
    >>> is_inside('foo.c', '')
 
236
    False
 
237
    >>> is_inside('', 'foo.c')
 
238
    True
 
239
    """
 
240
    # XXX: Most callers of this can actually do something smarter by 
 
241
    # looking at the inventory
 
242
    if dir == fname:
 
243
        return True
 
244
    
 
245
    if dir == '':
 
246
        return True
 
247
 
 
248
    if dir[-1] != '/':
 
249
        dir += '/'
 
250
 
 
251
    return fname.startswith(dir)
 
252
 
 
253
 
 
254
def is_inside_any(dir_list, fname):
 
255
    """True if fname is inside any of given dirs."""
 
256
    for dirname in dir_list:
 
257
        if is_inside(dirname, fname):
 
258
            return True
 
259
    else:
 
260
        return False
 
261
 
 
262
 
 
263
def pumpfile(fromfile, tofile):
 
264
    """Copy contents of one file to another."""
 
265
    BUFSIZE = 32768
 
266
    while True:
 
267
        b = fromfile.read(BUFSIZE)
 
268
        if not b:
 
269
            break
 
270
        tofile.write(b)
 
271
 
 
272
 
 
273
def sha_file(f):
 
274
    if hasattr(f, 'tell'):
 
275
        assert f.tell() == 0
 
276
    s = sha.new()
 
277
    BUFSIZE = 128<<10
 
278
    while True:
 
279
        b = f.read(BUFSIZE)
 
280
        if not b:
 
281
            break
 
282
        s.update(b)
 
283
    return s.hexdigest()
 
284
 
 
285
 
 
286
 
 
287
def sha_strings(strings):
 
288
    """Return the sha-1 of concatenation of strings"""
 
289
    s = sha.new()
 
290
    map(s.update, strings)
 
291
    return s.hexdigest()
 
292
 
 
293
 
 
294
def sha_string(f):
 
295
    s = sha.new()
 
296
    s.update(f)
 
297
    return s.hexdigest()
 
298
 
 
299
 
 
300
def fingerprint_file(f):
 
301
    s = sha.new()
 
302
    b = f.read()
 
303
    s.update(b)
 
304
    size = len(b)
 
305
    return {'size': size,
 
306
            'sha1': s.hexdigest()}
 
307
 
 
308
 
 
309
def compare_files(a, b):
 
310
    """Returns true if equal in contents"""
 
311
    BUFSIZE = 4096
 
312
    while True:
 
313
        ai = a.read(BUFSIZE)
 
314
        bi = b.read(BUFSIZE)
 
315
        if ai != bi:
 
316
            return False
 
317
        if ai == '':
 
318
            return True
 
319
 
 
320
 
 
321
def local_time_offset(t=None):
 
322
    """Return offset of local zone from GMT, either at present or at time t."""
 
323
    # python2.3 localtime() can't take None
 
324
    if t == None:
 
325
        t = time.time()
 
326
        
 
327
    if time.localtime(t).tm_isdst and time.daylight:
 
328
        return -time.altzone
 
329
    else:
 
330
        return -time.timezone
 
331
 
 
332
    
 
333
def format_date(t, offset=0, timezone='original', date_fmt=None, 
 
334
                show_offset=True):
 
335
    ## TODO: Perhaps a global option to use either universal or local time?
 
336
    ## Or perhaps just let people set $TZ?
 
337
    assert isinstance(t, float)
 
338
    
 
339
    if timezone == 'utc':
 
340
        tt = time.gmtime(t)
 
341
        offset = 0
 
342
    elif timezone == 'original':
 
343
        if offset == None:
 
344
            offset = 0
 
345
        tt = time.gmtime(t + offset)
 
346
    elif timezone == 'local':
 
347
        tt = time.localtime(t)
 
348
        offset = local_time_offset(t)
 
349
    else:
 
350
        raise BzrError("unsupported timezone format %r" % timezone,
 
351
                       ['options are "utc", "original", "local"'])
 
352
    if date_fmt is None:
 
353
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
 
354
    if show_offset:
 
355
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
 
356
    else:
 
357
        offset_str = ''
 
358
    return (time.strftime(date_fmt, tt) +  offset_str)
 
359
 
 
360
 
 
361
def compact_date(when):
 
362
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
 
363
    
 
364
 
 
365
 
 
366
def filesize(f):
 
367
    """Return size of given open file."""
 
368
    return os.fstat(f.fileno())[ST_SIZE]
 
369
 
 
370
# Define rand_bytes based on platform.
 
371
try:
 
372
    # Python 2.4 and later have os.urandom,
 
373
    # but it doesn't work on some arches
 
374
    os.urandom(1)
 
375
    rand_bytes = os.urandom
 
376
except (NotImplementedError, AttributeError):
 
377
    # If python doesn't have os.urandom, or it doesn't work,
 
378
    # then try to first pull random data from /dev/urandom
 
379
    if os.path.exists("/dev/urandom"):
 
380
        rand_bytes = file('/dev/urandom', 'rb').read
 
381
    # Otherwise, use this hack as a last resort
 
382
    else:
 
383
        # not well seeded, but better than nothing
 
384
        def rand_bytes(n):
 
385
            import random
 
386
            s = ''
 
387
            while n:
 
388
                s += chr(random.randint(0, 255))
 
389
                n -= 1
 
390
            return s
 
391
 
 
392
## TODO: We could later have path objects that remember their list
 
393
## decomposition (might be too tricksy though.)
 
394
 
 
395
def splitpath(p):
 
396
    """Turn string into list of parts.
 
397
 
 
398
    >>> splitpath('a')
 
399
    ['a']
 
400
    >>> splitpath('a/b')
 
401
    ['a', 'b']
 
402
    >>> splitpath('a/./b')
 
403
    ['a', 'b']
 
404
    >>> splitpath('a/.b')
 
405
    ['a', '.b']
 
406
    >>> splitpath('a/../b')
 
407
    Traceback (most recent call last):
 
408
    ...
 
409
    BzrError: sorry, '..' not allowed in path
 
410
    """
 
411
    assert isinstance(p, types.StringTypes)
 
412
 
 
413
    # split on either delimiter because people might use either on
 
414
    # Windows
 
415
    ps = re.split(r'[\\/]', p)
 
416
 
 
417
    rps = []
 
418
    for f in ps:
 
419
        if f == '..':
 
420
            raise BzrError("sorry, %r not allowed in path" % f)
 
421
        elif (f == '.') or (f == ''):
 
422
            pass
 
423
        else:
 
424
            rps.append(f)
 
425
    return rps
 
426
 
 
427
def joinpath(p):
 
428
    assert isinstance(p, list)
 
429
    for f in p:
 
430
        if (f == '..') or (f == None) or (f == ''):
 
431
            raise BzrError("sorry, %r not allowed in path" % f)
 
432
    return pathjoin(*p)
 
433
 
 
434
 
 
435
def appendpath(p1, p2):
 
436
    if p1 == '':
 
437
        return p2
 
438
    else:
 
439
        return pathjoin(p1, p2)
 
440
    
 
441
 
 
442
def split_lines(s):
 
443
    """Split s into lines, but without removing the newline characters."""
 
444
    return StringIO(s).readlines()
 
445
 
 
446
 
 
447
def hardlinks_good():
 
448
    return sys.platform not in ('win32', 'cygwin', 'darwin')
 
449
 
 
450
 
 
451
def link_or_copy(src, dest):
 
452
    """Hardlink a file, or copy it if it can't be hardlinked."""
 
453
    if not hardlinks_good():
 
454
        copyfile(src, dest)
 
455
        return
 
456
    try:
 
457
        os.link(src, dest)
 
458
    except (OSError, IOError), e:
 
459
        if e.errno != errno.EXDEV:
 
460
            raise
 
461
        copyfile(src, dest)
 
462
 
 
463
 
 
464
def has_symlinks():
 
465
    if hasattr(os, 'symlink'):
 
466
        return True
 
467
    else:
 
468
        return False
 
469
        
 
470
 
 
471
def contains_whitespace(s):
 
472
    """True if there are any whitespace characters in s."""
 
473
    for ch in string.whitespace:
 
474
        if ch in s:
 
475
            return True
 
476
    else:
 
477
        return False
 
478
 
 
479
 
 
480
def contains_linebreaks(s):
 
481
    """True if there is any vertical whitespace in s."""
 
482
    for ch in '\f\n\r':
 
483
        if ch in s:
 
484
            return True
 
485
    else:
 
486
        return False
 
487
 
 
488
 
 
489
def relpath(base, path):
 
490
    """Return path relative to base, or raise exception.
 
491
 
 
492
    The path may be either an absolute path or a path relative to the
 
493
    current working directory.
 
494
 
 
495
    os.path.commonprefix (python2.4) has a bad bug that it works just
 
496
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
 
497
    avoids that problem."""
 
498
    rp = abspath(path)
 
499
 
 
500
    s = []
 
501
    head = rp
 
502
    while len(head) >= len(base):
 
503
        if head == base:
 
504
            break
 
505
        head, tail = os.path.split(head)
 
506
        if tail:
 
507
            s.insert(0, tail)
 
508
    else:
 
509
        # XXX This should raise a NotChildPath exception, as its not tied
 
510
        # to branch anymore.
 
511
        raise PathNotChild(rp, base)
 
512
 
 
513
    if s:
 
514
        return pathjoin(*s)
 
515
    else:
 
516
        return ''
 
517
 
 
518
 
 
519
 
 
520
def terminal_width():
 
521
    """Return estimated terminal width."""
 
522
 
 
523
    # TODO: Do something smart on Windows?
 
524
 
 
525
    # TODO: Is there anything that gets a better update when the window
 
526
    # is resized while the program is running? We could use the Python termcap
 
527
    # library.
 
528
    try:
 
529
        return int(os.environ['COLUMNS'])
 
530
    except (IndexError, KeyError, ValueError):
 
531
        return 80