~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Robert Collins
  • Date: 2005-09-12 12:42:30 UTC
  • mfrom: (1092.2.9)
  • mto: (1092.2.15)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20050912124229-abcadea71750e4ab
revision eq operators

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
    os.chmod(filename, mod)
38
38
 
39
39
 
40
 
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
 
40
_QUOTE_RE = None
41
41
 
42
 
_SLASH_RE = re.compile(r'[\\/]+')
43
42
 
44
43
def quotefn(f):
45
44
    """Return a quoted filename filename
47
46
    This previously used backslash quoting, but that works poorly on
48
47
    Windows."""
49
48
    # TODO: I'm not really sure this is the best format either.x
 
49
    global _QUOTE_RE
 
50
    if _QUOTE_RE == None:
 
51
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
 
52
        
50
53
    if _QUOTE_RE.search(f):
51
54
        return '"' + f + '"'
52
55
    else:
75
78
    else:
76
79
        raise BzrError('invalid file kind %r' % kind)
77
80
 
 
81
def lexists(f):
 
82
    try:
 
83
        if hasattr(os, 'lstat'):
 
84
            os.lstat(f)
 
85
        else:
 
86
            os.stat(f)
 
87
        return True
 
88
    except OSError,e:
 
89
        if e.errno == errno.ENOENT:
 
90
            return False;
 
91
        else:
 
92
            raise BzrError("lstat/stat of (%r): %r" % (f, e))
78
93
 
 
94
def normalizepath(f):
 
95
    if hasattr(os.path, 'realpath'):
 
96
        F = os.path.realpath
 
97
    else:
 
98
        F = os.path.abspath
 
99
    [p,e] = os.path.split(f)
 
100
    if e == "" or e == "." or e == "..":
 
101
        return F(f)
 
102
    else:
 
103
        return os.path.join(F(p), e)
 
104
    
79
105
 
80
106
def backup_file(fn):
81
107
    """Copy a file to a backup.
131
157
    except OSError:
132
158
        return False
133
159
 
 
160
def islink(f):
 
161
    """True if f is a symlink."""
 
162
    try:
 
163
        return S_ISLNK(os.lstat(f)[ST_MODE])
 
164
    except OSError:
 
165
        return False
134
166
 
135
167
def is_inside(dir, fname):
136
168
    """True if fname is inside dir.
269
301
    return realname, (username + '@' + socket.gethostname())
270
302
 
271
303
 
272
 
def _get_user_id():
 
304
def _get_user_id(branch):
273
305
    """Return the full user id from a file or environment variable.
274
306
 
275
 
    TODO: Allow taking this from a file in the branch directory too
276
 
    for per-branch ids."""
 
307
    e.g. "John Hacker <jhacker@foo.org>"
 
308
 
 
309
    branch
 
310
        A branch to use for a per-branch configuration, or None.
 
311
 
 
312
    The following are searched in order:
 
313
 
 
314
    1. $BZREMAIL
 
315
    2. .bzr/email for this branch.
 
316
    3. ~/.bzr.conf/email
 
317
    4. $EMAIL
 
318
    """
277
319
    v = os.environ.get('BZREMAIL')
278
320
    if v:
279
321
        return v.decode(bzrlib.user_encoding)
 
322
 
 
323
    if branch:
 
324
        try:
 
325
            return (branch.controlfile("email", "r") 
 
326
                    .read()
 
327
                    .decode(bzrlib.user_encoding)
 
328
                    .rstrip("\r\n"))
 
329
        except IOError, e:
 
330
            if e.errno != errno.ENOENT:
 
331
                raise
 
332
        except BzrError, e:
 
333
            pass
280
334
    
281
335
    try:
282
336
        return (open(os.path.join(config_dir(), "email"))
294
348
        return None
295
349
 
296
350
 
297
 
def username():
 
351
def username(branch):
298
352
    """Return email-style username.
299
353
 
300
354
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
301
355
 
302
356
    TODO: Check it's reasonably well-formed.
303
357
    """
304
 
    v = _get_user_id()
 
358
    v = _get_user_id(branch)
305
359
    if v:
306
360
        return v
307
361
    
312
366
        return email
313
367
 
314
368
 
315
 
_EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+')
316
 
def user_email():
 
369
def user_email(branch):
317
370
    """Return just the email component of a username."""
318
 
    e = _get_user_id()
 
371
    e = _get_user_id(branch)
319
372
    if e:
320
 
        m = _EMAIL_RE.search(e)
 
373
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
321
374
        if not m:
322
375
            raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
323
376
        return m.group(0)
468
521
        raise
469
522
 
470
523
 
471
 
def _get_editor():
472
 
    """Return a sequence of possible editor binaries for the current platform"""
473
 
    e = _read_config_value("editor")
474
 
    if e is not None:
475
 
        yield e
476
 
        
477
 
    if os.name == "windows":
478
 
        yield "notepad.exe"
479
 
    elif os.name == "posix":
480
 
        try:
481
 
            yield os.environ["EDITOR"]
482
 
        except KeyError:
483
 
            yield "/usr/bin/vi"
484
 
 
485
 
 
486
 
def _run_editor(filename):
487
 
    """Try to execute an editor to edit the commit message. Returns True on success,
488
 
    False on failure"""
489
 
    for e in _get_editor():
490
 
        x = os.spawnvp(os.P_WAIT, e, (e, filename))
491
 
        if x == 0:
492
 
            return True
493
 
        elif x == 127:
494
 
            continue
495
 
        else:
496
 
            break
497
 
    raise BzrError("Could not start any editor. Please specify $EDITOR or use ~/.bzr.conf/editor")
498
 
    return False
499
 
                          
500
 
 
501
 
def get_text_message(infotext, ignoreline = "default"):
502
 
    import tempfile
503
 
    
504
 
    if ignoreline == "default":
505
 
        ignoreline = "-- This line and the following will be ignored --"
506
 
        
507
 
    try:
508
 
        tmp_fileno, msgfilename = tempfile.mkstemp()
509
 
        msgfile = os.close(tmp_fileno)
510
 
        if infotext is not None and infotext != "":
511
 
            hasinfo = True
512
 
            msgfile = file(msgfilename, "w")
513
 
            msgfile.write("\n\n%s\n\n%s" % (ignoreline, infotext))
514
 
            msgfile.close()
515
 
        else:
516
 
            hasinfo = False
517
 
 
518
 
        if not _run_editor(msgfilename):
519
 
            return None
520
 
        
521
 
        started = False
522
 
        msg = []
523
 
        lastline, nlines = 0, 0
524
 
        for line in file(msgfilename, "r"):
525
 
            stripped_line = line.strip()
526
 
            # strip empty line before the log message starts
527
 
            if not started:
528
 
                if stripped_line != "":
529
 
                    started = True
530
 
                else:
531
 
                    continue
532
 
            # check for the ignore line only if there
533
 
            # is additional information at the end
534
 
            if hasinfo and stripped_line == ignoreline:
535
 
                break
536
 
            nlines += 1
537
 
            # keep track of the last line that had some content
538
 
            if stripped_line != "":
539
 
                lastline = nlines
540
 
            msg.append(line)
541
 
            
542
 
        if len(msg) == 0:
543
 
            return None
544
 
        # delete empty lines at the end
545
 
        del msg[lastline:]
546
 
        # add a newline at the end, if needed
547
 
        if not msg[-1].endswith("\n"):
548
 
            return "%s%s" % ("".join(msg), "\n")
549
 
        else:
550
 
            return "".join(msg)
551
 
    finally:
552
 
        # delete the msg file in any case
553
 
        try: os.unlink(msgfilename)
554
 
        except IOError: pass