~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

- merge improved merge base selection from aaron
aaron.bentley@utoronto.ca-20050912025534-43d7275dd948e4ad

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:
139
142
    that . and .. and repeated slashes are eliminated, and the separators
140
143
    are canonical for the platform.
141
144
    
 
145
    The empty string as a dir name is taken as top-of-tree and matches 
 
146
    everything.
 
147
    
142
148
    >>> is_inside('src', 'src/foo.c')
143
149
    True
144
150
    >>> is_inside('src', 'srccontrol')
147
153
    True
148
154
    >>> is_inside('foo.c', 'foo.c')
149
155
    True
 
156
    >>> is_inside('foo.c', '')
 
157
    False
 
158
    >>> is_inside('', 'foo.c')
 
159
    True
150
160
    """
151
161
    # XXX: Most callers of this can actually do something smarter by 
152
162
    # looking at the inventory
153
 
 
154
163
    if dir == fname:
155
164
        return True
156
165
    
 
166
    if dir == '':
 
167
        return True
 
168
    
157
169
    if dir[-1] != os.sep:
158
170
        dir += os.sep
159
171
    
260
272
    return realname, (username + '@' + socket.gethostname())
261
273
 
262
274
 
263
 
def _get_user_id():
 
275
def _get_user_id(branch):
264
276
    """Return the full user id from a file or environment variable.
265
277
 
266
 
    TODO: Allow taking this from a file in the branch directory too
267
 
    for per-branch ids."""
 
278
    e.g. "John Hacker <jhacker@foo.org>"
 
279
 
 
280
    branch
 
281
        A branch to use for a per-branch configuration, or None.
 
282
 
 
283
    The following are searched in order:
 
284
 
 
285
    1. $BZREMAIL
 
286
    2. .bzr/email for this branch.
 
287
    3. ~/.bzr.conf/email
 
288
    4. $EMAIL
 
289
    """
268
290
    v = os.environ.get('BZREMAIL')
269
291
    if v:
270
292
        return v.decode(bzrlib.user_encoding)
 
293
 
 
294
    if branch:
 
295
        try:
 
296
            return (branch.controlfile("email", "r") 
 
297
                    .read()
 
298
                    .decode(bzrlib.user_encoding)
 
299
                    .rstrip("\r\n"))
 
300
        except IOError, e:
 
301
            if e.errno != errno.ENOENT:
 
302
                raise
 
303
        except BzrError, e:
 
304
            pass
271
305
    
272
306
    try:
273
307
        return (open(os.path.join(config_dir(), "email"))
285
319
        return None
286
320
 
287
321
 
288
 
def username():
 
322
def username(branch):
289
323
    """Return email-style username.
290
324
 
291
325
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
292
326
 
293
327
    TODO: Check it's reasonably well-formed.
294
328
    """
295
 
    v = _get_user_id()
 
329
    v = _get_user_id(branch)
296
330
    if v:
297
331
        return v
298
332
    
303
337
        return email
304
338
 
305
339
 
306
 
_EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+')
307
 
def user_email():
 
340
def user_email(branch):
308
341
    """Return just the email component of a username."""
309
 
    e = _get_user_id()
 
342
    e = _get_user_id(branch)
310
343
    if e:
311
 
        m = _EMAIL_RE.search(e)
 
344
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
312
345
        if not m:
313
346
            raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
314
347
        return m.group(0)
358
391
        tt = time.localtime(t)
359
392
        offset = local_time_offset(t)
360
393
    else:
361
 
        raise BzrError("unsupported timezone format %r",
362
 
                ['options are "utc", "original", "local"'])
 
394
        raise BzrError("unsupported timezone format %r" % timezone,
 
395
                       ['options are "utc", "original", "local"'])
363
396
 
364
397
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
365
398
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
459
492
        raise
460
493
 
461
494
 
462
 
def _get_editor():
463
 
    """Return a sequence of possible editor binaries for the current platform"""
464
 
    e = _read_config_value("editor")
465
 
    if e is not None:
466
 
        yield e
467
 
        
468
 
    if os.name == "windows":
469
 
        yield "notepad.exe"
470
 
    elif os.name == "posix":
471
 
        try:
472
 
            yield os.environ["EDITOR"]
473
 
        except KeyError:
474
 
            yield "/usr/bin/vi"
475
 
 
476
 
 
477
 
def _run_editor(filename):
478
 
    """Try to execute an editor to edit the commit message. Returns True on success,
479
 
    False on failure"""
480
 
    for e in _get_editor():
481
 
        x = os.spawnvp(os.P_WAIT, e, (e, filename))
482
 
        if x == 0:
483
 
            return True
484
 
        elif x == 127:
485
 
            continue
486
 
        else:
487
 
            break
488
 
    raise BzrError("Could not start any editor. Please specify $EDITOR or use ~/.bzr.conf/editor")
489
 
    return False
490
 
                          
491
 
 
492
 
def get_text_message(infotext, ignoreline = "default"):
493
 
    import tempfile
494
 
    
495
 
    if ignoreline == "default":
496
 
        ignoreline = "-- This line and the following will be ignored --"
497
 
        
498
 
    try:
499
 
        tmp_fileno, msgfilename = tempfile.mkstemp()
500
 
        msgfile = os.close(tmp_fileno)
501
 
        if infotext is not None and infotext != "":
502
 
            hasinfo = True
503
 
            msgfile = file(msgfilename, "w")
504
 
            msgfile.write("\n\n%s\n\n%s" % (ignoreline, infotext))
505
 
            msgfile.close()
506
 
        else:
507
 
            hasinfo = False
508
 
 
509
 
        if not _run_editor(msgfilename):
510
 
            return None
511
 
        
512
 
        started = False
513
 
        msg = []
514
 
        lastline, nlines = 0, 0
515
 
        for line in file(msgfilename, "r"):
516
 
            stripped_line = line.strip()
517
 
            # strip empty line before the log message starts
518
 
            if not started:
519
 
                if stripped_line != "":
520
 
                    started = True
521
 
                else:
522
 
                    continue
523
 
            # check for the ignore line only if there
524
 
            # is additional information at the end
525
 
            if hasinfo and stripped_line == ignoreline:
526
 
                break
527
 
            nlines += 1
528
 
            # keep track of the last line that had some content
529
 
            if stripped_line != "":
530
 
                lastline = nlines
531
 
            msg.append(line)
532
 
            
533
 
        if len(msg) == 0:
534
 
            return None
535
 
        # delete empty lines at the end
536
 
        del msg[lastline:]
537
 
        # add a newline at the end, if needed
538
 
        if not msg[-1].endswith("\n"):
539
 
            return "%s%s" % ("".join(msg), "\n")
540
 
        else:
541
 
            return "".join(msg)
542
 
    finally:
543
 
        # delete the msg file in any case
544
 
        try: os.unlink(msgfilename)
545
 
        except IOError: pass