~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Robert Collins
  • Date: 2005-10-03 02:10:02 UTC
  • mto: (1393.1.30)
  • mto: This revision was merged to the branch mainline in revision 1400.
  • Revision ID: robertc@robertcollins.net-20051003021002-4bf750e1d2bf7149
move checks for versionability of file kinds into InventoryEntry

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import os
25
25
import re
26
26
import sha
27
 
import string
28
27
import sys
29
28
import time
30
29
import types
31
30
 
32
31
import bzrlib
33
 
from bzrlib.errors import BzrError, NotBranchError
 
32
from bzrlib.errors import BzrError
34
33
from bzrlib.trace import mutter
35
34
 
36
35
 
119
118
        return F(f)
120
119
    else:
121
120
        return os.path.join(F(p), e)
122
 
 
123
 
if os.name == "posix":
124
 
    # In Python 2.4.2 and older, os.path.abspath and os.path.realpath
125
 
    # choke on a Unicode string containing a relative path if
126
 
    # os.getcwd() returns a non-sys.getdefaultencoding()-encoded
127
 
    # string.
128
 
    _fs_enc = sys.getfilesystemencoding()
129
 
    def abspath(path):
130
 
        return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
131
 
    def realpath(path):
132
 
        return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
133
 
else:
134
 
    # We need to use the Unicode-aware os.path.abspath and
135
 
    # os.path.realpath on Windows systems.
136
 
    abspath = os.path.abspath
137
 
    realpath = os.path.realpath
 
121
    
138
122
 
139
123
def backup_file(fn):
140
124
    """Copy a file to a backup.
147
131
        return
148
132
    bfn = fn + '~'
149
133
 
150
 
    if has_symlinks() and os.path.islink(fn):
151
 
        target = os.readlink(fn)
152
 
        os.symlink(target, bfn)
153
 
        return
154
134
    inf = file(fn, 'rb')
155
135
    try:
156
136
        content = inf.read()
240
220
 
241
221
def pumpfile(fromfile, tofile):
242
222
    """Copy contents of one file to another."""
243
 
    BUFSIZE = 32768
244
 
    while True:
245
 
        b = fromfile.read(BUFSIZE)
246
 
        if not b:
247
 
            break
248
 
        tofile.write(b)
 
223
    tofile.write(fromfile.read())
249
224
 
250
225
 
251
226
def sha_file(f):
284
259
            'sha1': s.hexdigest()}
285
260
 
286
261
 
 
262
def config_dir():
 
263
    """Return per-user configuration directory.
 
264
 
 
265
    By default this is ~/.bzr.conf/
 
266
    
 
267
    TODO: Global option --config-dir to override this.
 
268
    """
 
269
    return os.path.join(os.path.expanduser("~"), ".bzr.conf")
 
270
 
 
271
 
 
272
def _auto_user_id():
 
273
    """Calculate automatic user identification.
 
274
 
 
275
    Returns (realname, email).
 
276
 
 
277
    Only used when none is set in the environment or the id file.
 
278
 
 
279
    This previously used the FQDN as the default domain, but that can
 
280
    be very slow on machines where DNS is broken.  So now we simply
 
281
    use the hostname.
 
282
    """
 
283
    import socket
 
284
 
 
285
    # XXX: Any good way to get real user name on win32?
 
286
 
 
287
    try:
 
288
        import pwd
 
289
        uid = os.getuid()
 
290
        w = pwd.getpwuid(uid)
 
291
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
292
        username = w.pw_name.decode(bzrlib.user_encoding)
 
293
        comma = gecos.find(',')
 
294
        if comma == -1:
 
295
            realname = gecos
 
296
        else:
 
297
            realname = gecos[:comma]
 
298
        if not realname:
 
299
            realname = username
 
300
 
 
301
    except ImportError:
 
302
        import getpass
 
303
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
304
 
 
305
    return realname, (username + '@' + socket.gethostname())
 
306
 
 
307
 
 
308
def _get_user_id(branch):
 
309
    """Return the full user id from a file or environment variable.
 
310
 
 
311
    e.g. "John Hacker <jhacker@foo.org>"
 
312
 
 
313
    branch
 
314
        A branch to use for a per-branch configuration, or None.
 
315
 
 
316
    The following are searched in order:
 
317
 
 
318
    1. $BZREMAIL
 
319
    2. .bzr/email for this branch.
 
320
    3. ~/.bzr.conf/email
 
321
    4. $EMAIL
 
322
    """
 
323
    v = os.environ.get('BZREMAIL')
 
324
    if v:
 
325
        return v.decode(bzrlib.user_encoding)
 
326
 
 
327
    if branch:
 
328
        try:
 
329
            return (branch.controlfile("email", "r") 
 
330
                    .read()
 
331
                    .decode(bzrlib.user_encoding)
 
332
                    .rstrip("\r\n"))
 
333
        except IOError, e:
 
334
            if e.errno != errno.ENOENT:
 
335
                raise
 
336
        except BzrError, e:
 
337
            pass
 
338
    
 
339
    try:
 
340
        return (open(os.path.join(config_dir(), "email"))
 
341
                .read()
 
342
                .decode(bzrlib.user_encoding)
 
343
                .rstrip("\r\n"))
 
344
    except IOError, e:
 
345
        if e.errno != errno.ENOENT:
 
346
            raise e
 
347
 
 
348
    v = os.environ.get('EMAIL')
 
349
    if v:
 
350
        return v.decode(bzrlib.user_encoding)
 
351
    else:    
 
352
        return None
 
353
 
 
354
 
 
355
def username(branch):
 
356
    """Return email-style username.
 
357
 
 
358
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
359
 
 
360
    TODO: Check it's reasonably well-formed.
 
361
    """
 
362
    v = _get_user_id(branch)
 
363
    if v:
 
364
        return v
 
365
    
 
366
    name, email = _auto_user_id()
 
367
    if name:
 
368
        return '%s <%s>' % (name, email)
 
369
    else:
 
370
        return email
 
371
 
 
372
 
 
373
def user_email(branch):
 
374
    """Return just the email component of a username."""
 
375
    e = _get_user_id(branch)
 
376
    if e:
 
377
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
378
        if not m:
 
379
            raise BzrError("%r doesn't seem to contain "
 
380
                           "a reasonable email address" % e)
 
381
        return m.group(0)
 
382
 
 
383
    return _auto_user_id()[1]
 
384
 
 
385
 
287
386
def compare_files(a, b):
288
387
    """Returns true if equal in contents"""
289
388
    BUFSIZE = 4096
308
407
        return -time.timezone
309
408
 
310
409
    
311
 
def format_date(t, offset=0, timezone='original', date_fmt=None, 
312
 
                show_offset=True):
 
410
def format_date(t, offset=0, timezone='original'):
313
411
    ## TODO: Perhaps a global option to use either universal or local time?
314
412
    ## Or perhaps just let people set $TZ?
315
413
    assert isinstance(t, float)
327
425
    else:
328
426
        raise BzrError("unsupported timezone format %r" % timezone,
329
427
                       ['options are "utc", "original", "local"'])
330
 
    if date_fmt is None:
331
 
        date_fmt = "%a %Y-%m-%d %H:%M:%S"
332
 
    if show_offset:
333
 
        offset_str = ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)
334
 
    else:
335
 
        offset_str = ''
336
 
    return (time.strftime(date_fmt, tt) +  offset_str)
 
428
 
 
429
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
 
430
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
337
431
 
338
432
 
339
433
def compact_date(when):
417
511
        return os.path.join(p1, p2)
418
512
    
419
513
 
 
514
def _read_config_value(name):
 
515
    """Read a config value from the file ~/.bzr.conf/<name>
 
516
    Return None if the file does not exist"""
 
517
    try:
 
518
        f = file(os.path.join(config_dir(), name), "r")
 
519
        return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
 
520
    except IOError, e:
 
521
        if e.errno == errno.ENOENT:
 
522
            return None
 
523
        raise
 
524
 
 
525
 
420
526
def split_lines(s):
421
527
    """Split s into lines, but without removing the newline characters."""
422
528
    return StringIO(s).readlines()
437
543
        if e.errno != errno.EXDEV:
438
544
            raise
439
545
        copyfile(src, dest)
440
 
 
441
 
 
442
 
def has_symlinks():
443
 
    if hasattr(os, 'symlink'):
444
 
        return True
445
 
    else:
446
 
        return False
447
 
        
448
 
 
449
 
def contains_whitespace(s):
450
 
    """True if there are any whitespace characters in s."""
451
 
    for ch in string.whitespace:
452
 
        if ch in s:
453
 
            return True
454
 
    else:
455
 
        return False
456
 
 
457
 
 
458
 
def contains_linebreaks(s):
459
 
    """True if there is any vertical whitespace in s."""
460
 
    for ch in '\f\n\r':
461
 
        if ch in s:
462
 
            return True
463
 
    else:
464
 
        return False
465
 
 
466
 
 
467
 
def relpath(base, path):
468
 
    """Return path relative to base, or raise exception.
469
 
 
470
 
    The path may be either an absolute path or a path relative to the
471
 
    current working directory.
472
 
 
473
 
    os.path.commonprefix (python2.4) has a bad bug that it works just
474
 
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
475
 
    avoids that problem."""
476
 
    rp = abspath(path)
477
 
 
478
 
    s = []
479
 
    head = rp
480
 
    while len(head) >= len(base):
481
 
        if head == base:
482
 
            break
483
 
        head, tail = os.path.split(head)
484
 
        if tail:
485
 
            s.insert(0, tail)
486
 
    else:
487
 
        # XXX This should raise a NotChildPath exception, as its not tied
488
 
        # to branch anymore.
489
 
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
490
 
 
491
 
    return os.sep.join(s)
492
 
 
493
 
 
494
 
 
495
 
def terminal_width():
496
 
    """Return estimated terminal width."""
497
 
 
498
 
    # TODO: Do something smart on Windows?
499
 
 
500
 
    # TODO: Is there anything that gets a better update when the window
501
 
    # is resized while the program is running? We could use the Python termcap
502
 
    # library.
503
 
    try:
504
 
        return int(os.environ['COLUMNS'])
505
 
    except (IndexError, KeyError, ValueError):
506
 
        return 80