/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: Robert Collins
  • Date: 2005-09-26 08:56:15 UTC
  • mto: (1092.3.4)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: robertc@robertcollins.net-20050926085615-99b8fb35f41b541d
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid

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 stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
 
20
                  S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
 
21
import errno
 
22
import os
 
23
import re
 
24
import sha
 
25
import sys
 
26
import time
 
27
import types
 
28
 
 
29
import bzrlib
 
30
from bzrlib.errors import BzrError
 
31
from bzrlib.trace import mutter
 
32
 
 
33
 
 
34
def make_readonly(filename):
 
35
    """Make a filename read-only."""
 
36
    mod = os.stat(filename).st_mode
 
37
    mod = mod & 0777555
 
38
    os.chmod(filename, mod)
 
39
 
 
40
 
 
41
def make_writable(filename):
 
42
    mod = os.stat(filename).st_mode
 
43
    mod = mod | 0200
 
44
    os.chmod(filename, mod)
 
45
 
 
46
 
 
47
_QUOTE_RE = None
 
48
 
 
49
 
 
50
def quotefn(f):
 
51
    """Return a quoted filename filename
 
52
 
 
53
    This previously used backslash quoting, but that works poorly on
 
54
    Windows."""
 
55
    # TODO: I'm not really sure this is the best format either.x
 
56
    global _QUOTE_RE
 
57
    if _QUOTE_RE == None:
 
58
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/\\_~-])')
 
59
        
 
60
    if _QUOTE_RE.search(f):
 
61
        return '"' + f + '"'
 
62
    else:
 
63
        return f
 
64
 
 
65
 
 
66
def file_kind(f):
 
67
    mode = os.lstat(f)[ST_MODE]
 
68
    if S_ISREG(mode):
 
69
        return 'file'
 
70
    elif S_ISDIR(mode):
 
71
        return 'directory'
 
72
    elif S_ISLNK(mode):
 
73
        return 'symlink'
 
74
    elif S_ISCHR(mode):
 
75
        return 'chardev'
 
76
    elif S_ISBLK(mode):
 
77
        return 'block'
 
78
    elif S_ISFIFO(mode):
 
79
        return 'fifo'
 
80
    elif S_ISSOCK(mode):
 
81
        return 'socket'
 
82
    else:
 
83
        return 'unknown'
 
84
 
 
85
 
 
86
def kind_marker(kind):
 
87
    if kind == 'file':
 
88
        return ''
 
89
    elif kind == 'directory':
 
90
        return '/'
 
91
    elif kind == 'symlink':
 
92
        return '@'
 
93
    else:
 
94
        raise BzrError('invalid file kind %r' % kind)
 
95
 
 
96
 
 
97
def backup_file(fn):
 
98
    """Copy a file to a backup.
 
99
 
 
100
    Backups are named in GNU-style, with a ~ suffix.
 
101
 
 
102
    If the file is already a backup, it's not copied.
 
103
    """
 
104
    if fn[-1] == '~':
 
105
        return
 
106
    bfn = fn + '~'
 
107
 
 
108
    inf = file(fn, 'rb')
 
109
    try:
 
110
        content = inf.read()
 
111
    finally:
 
112
        inf.close()
 
113
    
 
114
    outf = file(bfn, 'wb')
 
115
    try:
 
116
        outf.write(content)
 
117
    finally:
 
118
        outf.close()
 
119
 
 
120
if os.name == 'nt':
 
121
    import shutil
 
122
    rename = shutil.move
 
123
else:
 
124
    rename = os.rename
 
125
 
 
126
 
 
127
def isdir(f):
 
128
    """True if f is an accessible directory."""
 
129
    try:
 
130
        return S_ISDIR(os.lstat(f)[ST_MODE])
 
131
    except OSError:
 
132
        return False
 
133
 
 
134
 
 
135
def isfile(f):
 
136
    """True if f is a regular file."""
 
137
    try:
 
138
        return S_ISREG(os.lstat(f)[ST_MODE])
 
139
    except OSError:
 
140
        return False
 
141
 
 
142
 
 
143
def is_inside(dir, fname):
 
144
    """True if fname is inside dir.
 
145
    
 
146
    The parameters should typically be passed to os.path.normpath first, so
 
147
    that . and .. and repeated slashes are eliminated, and the separators
 
148
    are canonical for the platform.
 
149
    
 
150
    The empty string as a dir name is taken as top-of-tree and matches 
 
151
    everything.
 
152
    
 
153
    >>> is_inside('src', os.path.join('src', 'foo.c'))
 
154
    True
 
155
    >>> is_inside('src', 'srccontrol')
 
156
    False
 
157
    >>> is_inside('src', os.path.join('src', 'a', 'a', 'a', 'foo.c'))
 
158
    True
 
159
    >>> is_inside('foo.c', 'foo.c')
 
160
    True
 
161
    >>> is_inside('foo.c', '')
 
162
    False
 
163
    >>> is_inside('', 'foo.c')
 
164
    True
 
165
    """
 
166
    # XXX: Most callers of this can actually do something smarter by 
 
167
    # looking at the inventory
 
168
    if dir == fname:
 
169
        return True
 
170
    
 
171
    if dir == '':
 
172
        return True
 
173
 
 
174
    if dir[-1] != os.sep:
 
175
        dir += os.sep
 
176
 
 
177
    return fname.startswith(dir)
 
178
 
 
179
 
 
180
def is_inside_any(dir_list, fname):
 
181
    """True if fname is inside any of given dirs."""
 
182
    for dirname in dir_list:
 
183
        if is_inside(dirname, fname):
 
184
            return True
 
185
    else:
 
186
        return False
 
187
 
 
188
 
 
189
def pumpfile(fromfile, tofile):
 
190
    """Copy contents of one file to another."""
 
191
    tofile.write(fromfile.read())
 
192
 
 
193
 
 
194
def sha_file(f):
 
195
    if hasattr(f, 'tell'):
 
196
        assert f.tell() == 0
 
197
    s = sha.new()
 
198
    BUFSIZE = 128<<10
 
199
    while True:
 
200
        b = f.read(BUFSIZE)
 
201
        if not b:
 
202
            break
 
203
        s.update(b)
 
204
    return s.hexdigest()
 
205
 
 
206
 
 
207
def sha_string(f):
 
208
    s = sha.new()
 
209
    s.update(f)
 
210
    return s.hexdigest()
 
211
 
 
212
 
 
213
def fingerprint_file(f):
 
214
    s = sha.new()
 
215
    b = f.read()
 
216
    s.update(b)
 
217
    size = len(b)
 
218
    return {'size': size,
 
219
            'sha1': s.hexdigest()}
 
220
 
 
221
 
 
222
def config_dir():
 
223
    """Return per-user configuration directory.
 
224
 
 
225
    By default this is ~/.bzr.conf/
 
226
    
 
227
    TODO: Global option --config-dir to override this.
 
228
    """
 
229
    return os.path.join(os.path.expanduser("~"), ".bzr.conf")
 
230
 
 
231
 
 
232
def _auto_user_id():
 
233
    """Calculate automatic user identification.
 
234
 
 
235
    Returns (realname, email).
 
236
 
 
237
    Only used when none is set in the environment or the id file.
 
238
 
 
239
    This previously used the FQDN as the default domain, but that can
 
240
    be very slow on machines where DNS is broken.  So now we simply
 
241
    use the hostname.
 
242
    """
 
243
    import socket
 
244
 
 
245
    # XXX: Any good way to get real user name on win32?
 
246
 
 
247
    try:
 
248
        import pwd
 
249
        uid = os.getuid()
 
250
        w = pwd.getpwuid(uid)
 
251
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
252
        username = w.pw_name.decode(bzrlib.user_encoding)
 
253
        comma = gecos.find(',')
 
254
        if comma == -1:
 
255
            realname = gecos
 
256
        else:
 
257
            realname = gecos[:comma]
 
258
        if not realname:
 
259
            realname = username
 
260
 
 
261
    except ImportError:
 
262
        import getpass
 
263
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
264
 
 
265
    return realname, (username + '@' + socket.gethostname())
 
266
 
 
267
 
 
268
def _get_user_id(branch):
 
269
    """Return the full user id from a file or environment variable.
 
270
 
 
271
    e.g. "John Hacker <jhacker@foo.org>"
 
272
 
 
273
    branch
 
274
        A branch to use for a per-branch configuration, or None.
 
275
 
 
276
    The following are searched in order:
 
277
 
 
278
    1. $BZREMAIL
 
279
    2. .bzr/email for this branch.
 
280
    3. ~/.bzr.conf/email
 
281
    4. $EMAIL
 
282
    """
 
283
    v = os.environ.get('BZREMAIL')
 
284
    if v:
 
285
        return v.decode(bzrlib.user_encoding)
 
286
 
 
287
    if branch:
 
288
        try:
 
289
            return (branch.controlfile("email", "r") 
 
290
                    .read()
 
291
                    .decode(bzrlib.user_encoding)
 
292
                    .rstrip("\r\n"))
 
293
        except IOError, e:
 
294
            if e.errno != errno.ENOENT:
 
295
                raise
 
296
        except BzrError, e:
 
297
            pass
 
298
    
 
299
    try:
 
300
        return (open(os.path.join(config_dir(), "email"))
 
301
                .read()
 
302
                .decode(bzrlib.user_encoding)
 
303
                .rstrip("\r\n"))
 
304
    except IOError, e:
 
305
        if e.errno != errno.ENOENT:
 
306
            raise e
 
307
 
 
308
    v = os.environ.get('EMAIL')
 
309
    if v:
 
310
        return v.decode(bzrlib.user_encoding)
 
311
    else:    
 
312
        return None
 
313
 
 
314
 
 
315
def username(branch):
 
316
    """Return email-style username.
 
317
 
 
318
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
319
 
 
320
    TODO: Check it's reasonably well-formed.
 
321
    """
 
322
    v = _get_user_id(branch)
 
323
    if v:
 
324
        return v
 
325
    
 
326
    name, email = _auto_user_id()
 
327
    if name:
 
328
        return '%s <%s>' % (name, email)
 
329
    else:
 
330
        return email
 
331
 
 
332
 
 
333
def user_email(branch):
 
334
    """Return just the email component of a username."""
 
335
    e = _get_user_id(branch)
 
336
    if e:
 
337
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
338
        if not m:
 
339
            raise BzrError("%r doesn't seem to contain "
 
340
                           "a reasonable email address" % e)
 
341
        return m.group(0)
 
342
 
 
343
    return _auto_user_id()[1]
 
344
 
 
345
 
 
346
def compare_files(a, b):
 
347
    """Returns true if equal in contents"""
 
348
    BUFSIZE = 4096
 
349
    while True:
 
350
        ai = a.read(BUFSIZE)
 
351
        bi = b.read(BUFSIZE)
 
352
        if ai != bi:
 
353
            return False
 
354
        if ai == '':
 
355
            return True
 
356
 
 
357
 
 
358
def local_time_offset(t=None):
 
359
    """Return offset of local zone from GMT, either at present or at time t."""
 
360
    # python2.3 localtime() can't take None
 
361
    if t == None:
 
362
        t = time.time()
 
363
        
 
364
    if time.localtime(t).tm_isdst and time.daylight:
 
365
        return -time.altzone
 
366
    else:
 
367
        return -time.timezone
 
368
 
 
369
    
 
370
def format_date(t, offset=0, timezone='original'):
 
371
    ## TODO: Perhaps a global option to use either universal or local time?
 
372
    ## Or perhaps just let people set $TZ?
 
373
    assert isinstance(t, float)
 
374
    
 
375
    if timezone == 'utc':
 
376
        tt = time.gmtime(t)
 
377
        offset = 0
 
378
    elif timezone == 'original':
 
379
        if offset == None:
 
380
            offset = 0
 
381
        tt = time.gmtime(t + offset)
 
382
    elif timezone == 'local':
 
383
        tt = time.localtime(t)
 
384
        offset = local_time_offset(t)
 
385
    else:
 
386
        raise BzrError("unsupported timezone format %r" % timezone,
 
387
                       ['options are "utc", "original", "local"'])
 
388
 
 
389
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
 
390
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
 
391
 
 
392
 
 
393
def compact_date(when):
 
394
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
 
395
    
 
396
 
 
397
 
 
398
def filesize(f):
 
399
    """Return size of given open file."""
 
400
    return os.fstat(f.fileno())[ST_SIZE]
 
401
 
 
402
# Define rand_bytes based on platform.
 
403
try:
 
404
    # Python 2.4 and later have os.urandom,
 
405
    # but it doesn't work on some arches
 
406
    os.urandom(1)
 
407
    rand_bytes = os.urandom
 
408
except (NotImplementedError, AttributeError):
 
409
    # If python doesn't have os.urandom, or it doesn't work,
 
410
    # then try to first pull random data from /dev/urandom
 
411
    if os.path.exists("/dev/urandom"):
 
412
        rand_bytes = file('/dev/urandom', 'rb').read
 
413
    # Otherwise, use this hack as a last resort
 
414
    else:
 
415
        # not well seeded, but better than nothing
 
416
        def rand_bytes(n):
 
417
            import random
 
418
            s = ''
 
419
            while n:
 
420
                s += chr(random.randint(0, 255))
 
421
                n -= 1
 
422
            return s
 
423
 
 
424
## TODO: We could later have path objects that remember their list
 
425
## decomposition (might be too tricksy though.)
 
426
 
 
427
def splitpath(p):
 
428
    """Turn string into list of parts.
 
429
 
 
430
    >>> splitpath('a')
 
431
    ['a']
 
432
    >>> splitpath('a/b')
 
433
    ['a', 'b']
 
434
    >>> splitpath('a/./b')
 
435
    ['a', 'b']
 
436
    >>> splitpath('a/.b')
 
437
    ['a', '.b']
 
438
    >>> splitpath('a/../b')
 
439
    Traceback (most recent call last):
 
440
    ...
 
441
    BzrError: sorry, '..' not allowed in path
 
442
    """
 
443
    assert isinstance(p, types.StringTypes)
 
444
 
 
445
    # split on either delimiter because people might use either on
 
446
    # Windows
 
447
    ps = re.split(r'[\\/]', p)
 
448
 
 
449
    rps = []
 
450
    for f in ps:
 
451
        if f == '..':
 
452
            raise BzrError("sorry, %r not allowed in path" % f)
 
453
        elif (f == '.') or (f == ''):
 
454
            pass
 
455
        else:
 
456
            rps.append(f)
 
457
    return rps
 
458
 
 
459
def joinpath(p):
 
460
    assert isinstance(p, list)
 
461
    for f in p:
 
462
        if (f == '..') or (f == None) or (f == ''):
 
463
            raise BzrError("sorry, %r not allowed in path" % f)
 
464
    return os.path.join(*p)
 
465
 
 
466
 
 
467
def appendpath(p1, p2):
 
468
    if p1 == '':
 
469
        return p2
 
470
    else:
 
471
        return os.path.join(p1, p2)
 
472
    
 
473
 
 
474
def extern_command(cmd, ignore_errors = False):
 
475
    mutter('external command: %s' % `cmd`)
 
476
    if os.system(cmd):
 
477
        if not ignore_errors:
 
478
            raise BzrError('command failed')
 
479
 
 
480
 
 
481
def _read_config_value(name):
 
482
    """Read a config value from the file ~/.bzr.conf/<name>
 
483
    Return None if the file does not exist"""
 
484
    try:
 
485
        f = file(os.path.join(config_dir(), name), "r")
 
486
        return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
 
487
    except IOError, e:
 
488
        if e.errno == errno.ENOENT:
 
489
            return None
 
490
        raise