~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Pool
  • Date: 2005-06-24 07:06:38 UTC
  • Revision ID: mbp@sourcefrog.net-20050624070638-4b1230afde50b1a8
- files are only reported as modified if their name or parent has changed,
  not if their parent is renamed

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import os, types, re, time, errno, sys
20
20
from stat import S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE
21
21
 
22
 
from errors import bailout, BzrError
23
 
from trace import mutter
 
22
from bzrlib.errors import BzrError
 
23
from bzrlib.trace import mutter
24
24
import bzrlib
25
25
 
26
26
def make_readonly(filename):
56
56
    elif S_ISLNK(mode):
57
57
        return 'symlink'
58
58
    else:
59
 
        raise BzrError("can't handle file kind with mode %o of %r" % (mode, f)) 
 
59
        raise BzrError("can't handle file kind with mode %o of %r" % (mode, f))
 
60
 
 
61
 
 
62
def kind_marker(kind):
 
63
    if kind == 'file':
 
64
        return ''
 
65
    elif kind == 'directory':
 
66
        return '/'
 
67
    elif kind == 'symlink':
 
68
        return '@'
 
69
    else:
 
70
        raise BzrError('invalid file kind %r' % kind)
60
71
 
61
72
 
62
73
 
77
88
        return False
78
89
 
79
90
 
 
91
def is_inside(dir, fname):
 
92
    """True if fname is inside dir.
 
93
    """
 
94
    return os.path.commonprefix([dir, fname]) == dir
 
95
 
 
96
 
 
97
def is_inside_any(dir_list, fname):
 
98
    """True if fname is inside any of given dirs."""
 
99
    # quick scan for perfect match
 
100
    if fname in dir_list:
 
101
        return True
 
102
    
 
103
    for dirname in dir_list:
 
104
        if is_inside(dirname, fname):
 
105
            return True
 
106
    else:
 
107
        return False
 
108
 
 
109
 
80
110
def pumpfile(fromfile, tofile):
81
111
    """Copy contents of one file to another."""
82
112
    tofile.write(fromfile.read())
218
248
    if e:
219
249
        m = _EMAIL_RE.search(e)
220
250
        if not m:
221
 
            bailout("%r doesn't seem to contain a reasonable email address" % e)
 
251
            raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
222
252
        return m.group(0)
223
253
 
224
254
    return _auto_user_id()[1]
266
296
        tt = time.localtime(t)
267
297
        offset = local_time_offset(t)
268
298
    else:
269
 
        bailout("unsupported timezone format %r",
 
299
        raise BzrError("unsupported timezone format %r",
270
300
                ['options are "utc", "original", "local"'])
271
301
 
272
302
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
315
345
    >>> splitpath('a/../b')
316
346
    Traceback (most recent call last):
317
347
    ...
318
 
    BzrError: ("sorry, '..' not allowed in path", [])
 
348
    BzrError: sorry, '..' not allowed in path
319
349
    """
320
350
    assert isinstance(p, types.StringTypes)
321
351
 
326
356
    rps = []
327
357
    for f in ps:
328
358
        if f == '..':
329
 
            bailout("sorry, %r not allowed in path" % f)
 
359
            raise BzrError("sorry, %r not allowed in path" % f)
330
360
        elif (f == '.') or (f == ''):
331
361
            pass
332
362
        else:
337
367
    assert isinstance(p, list)
338
368
    for f in p:
339
369
        if (f == '..') or (f == None) or (f == ''):
340
 
            bailout("sorry, %r not allowed in path" % f)
 
370
            raise BzrError("sorry, %r not allowed in path" % f)
341
371
    return os.path.join(*p)
342
372
 
343
373
 
352
382
    mutter('external command: %s' % `cmd`)
353
383
    if os.system(cmd):
354
384
        if not ignore_errors:
355
 
            bailout('command failed')
356
 
 
 
385
            raise BzrError('command failed')
 
386
 
 
387
 
 
388
def _read_config_value(name):
 
389
    """Read a config value from the file ~/.bzr.conf/<name>
 
390
    Return None if the file does not exist"""
 
391
    try:
 
392
        f = file(os.path.join(config_dir(), name), "r")
 
393
        return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
 
394
    except IOError, e:
 
395
        if e.errno == errno.ENOENT:
 
396
            return None
 
397
        raise
 
398
 
 
399
 
 
400
def _get_editor():
 
401
    """Return a sequence of possible editor binaries for the current platform"""
 
402
    e = _read_config_value("editor")
 
403
    if e is not None:
 
404
        yield e
 
405
        
 
406
    if os.name == "windows":
 
407
        yield "notepad.exe"
 
408
    elif os.name == "posix":
 
409
        try:
 
410
            yield os.environ["EDITOR"]
 
411
        except KeyError:
 
412
            yield "/usr/bin/vi"
 
413
 
 
414
 
 
415
def _run_editor(filename):
 
416
    """Try to execute an editor to edit the commit message. Returns True on success,
 
417
    False on failure"""
 
418
    for e in _get_editor():
 
419
        x = os.spawnvp(os.P_WAIT, e, (e, filename))
 
420
        if x == 0:
 
421
            return True
 
422
        elif x == 127:
 
423
            continue
 
424
        else:
 
425
            break
 
426
    raise BzrError("Could not start any editor. Please specify $EDITOR or use ~/.bzr.conf/editor")
 
427
    return False
 
428
                          
 
429
 
 
430
def get_text_message(infotext, ignoreline = "default"):
 
431
    import tempfile
 
432
    
 
433
    if ignoreline == "default":
 
434
        ignoreline = "-- This line and the following will be ignored --"
 
435
        
 
436
    try:
 
437
        tmp_fileno, msgfilename = tempfile.mkstemp()
 
438
        msgfile = os.close(tmp_fileno)
 
439
        if infotext is not None and infotext != "":
 
440
            hasinfo = True
 
441
            msgfile = file(msgfilename, "w")
 
442
            msgfile.write("\n\n%s\n\n%s" % (ignoreline, infotext))
 
443
            msgfile.close()
 
444
        else:
 
445
            hasinfo = False
 
446
 
 
447
        if not _run_editor(msgfilename):
 
448
            return None
 
449
        
 
450
        started = False
 
451
        msg = []
 
452
        lastline, nlines = 0, 0
 
453
        for line in file(msgfilename, "r"):
 
454
            stripped_line = line.strip()
 
455
            # strip empty line before the log message starts
 
456
            if not started:
 
457
                if stripped_line != "":
 
458
                    started = True
 
459
                else:
 
460
                    continue
 
461
            # check for the ignore line only if there
 
462
            # is additional information at the end
 
463
            if hasinfo and stripped_line == ignoreline:
 
464
                break
 
465
            nlines += 1
 
466
            # keep track of the last line that had some content
 
467
            if stripped_line != "":
 
468
                lastline = nlines
 
469
            msg.append(line)
 
470
            
 
471
        if len(msg) == 0:
 
472
            return None
 
473
        # delete empty lines at the end
 
474
        del msg[lastline:]
 
475
        # add a newline at the end, if needed
 
476
        if not msg[-1].endswith("\n"):
 
477
            return "%s%s" % ("".join(msg), "\n")
 
478
        else:
 
479
            return "".join(msg)
 
480
    finally:
 
481
        # delete the msg file in any case
 
482
        try: os.unlink(msgfilename)
 
483
        except IOError: pass