~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Pool
  • Date: 2005-09-22 06:28:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050922062855-a29aa53982b752d6
- try to avoid checking texts repeatedly

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
19
import os, types, re, time, errno, sys
 
20
import sha
 
21
from cStringIO import StringIO
 
22
 
20
23
from stat import S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE
21
24
 
22
 
from errors import bailout, BzrError
23
 
from trace import mutter
 
25
from bzrlib.errors import BzrError
 
26
from bzrlib.trace import mutter
24
27
import bzrlib
25
28
 
26
29
def make_readonly(filename):
37
40
    os.chmod(filename, mod)
38
41
 
39
42
 
40
 
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
 
43
_QUOTE_RE = None
 
44
 
 
45
 
41
46
def quotefn(f):
42
 
    """Return shell-quoted filename"""
43
 
    ## We could be a bit more terse by using double-quotes etc
44
 
    f = _QUOTE_RE.sub(r'\\\1', f)
45
 
    if f[0] == '~':
46
 
        f[0:1] = r'\~' 
47
 
    return f
 
47
    """Return a quoted filename filename
 
48
 
 
49
    This previously used backslash quoting, but that works poorly on
 
50
    Windows."""
 
51
    # TODO: I'm not really sure this is the best format either.x
 
52
    global _QUOTE_RE
 
53
    if _QUOTE_RE == None:
 
54
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
 
55
        
 
56
    if _QUOTE_RE.search(f):
 
57
        return '"' + f + '"'
 
58
    else:
 
59
        return f
48
60
 
49
61
 
50
62
def file_kind(f):
71
83
 
72
84
 
73
85
 
 
86
def backup_file(fn):
 
87
    """Copy a file to a backup.
 
88
 
 
89
    Backups are named in GNU-style, with a ~ suffix.
 
90
 
 
91
    If the file is already a backup, it's not copied.
 
92
    """
 
93
    if fn[-1] == '~':
 
94
        return
 
95
    bfn = fn + '~'
 
96
 
 
97
    inf = file(fn, 'rb')
 
98
    try:
 
99
        content = inf.read()
 
100
    finally:
 
101
        inf.close()
 
102
    
 
103
    outf = file(bfn, 'wb')
 
104
    try:
 
105
        outf.write(content)
 
106
    finally:
 
107
        outf.close()
 
108
 
 
109
def rename(path_from, path_to):
 
110
    """Basically the same as os.rename() just special for win32"""
 
111
    if sys.platform == 'win32':
 
112
        try:
 
113
            os.remove(path_to)
 
114
        except OSError, e:
 
115
            if e.errno != e.ENOENT:
 
116
                raise
 
117
    os.rename(path_from, path_to)
 
118
 
 
119
 
 
120
 
 
121
 
 
122
 
74
123
def isdir(f):
75
124
    """True if f is an accessible directory."""
76
125
    try:
90
139
 
91
140
def is_inside(dir, fname):
92
141
    """True if fname is inside dir.
 
142
    
 
143
    The parameters should typically be passed to os.path.normpath first, so
 
144
    that . and .. and repeated slashes are eliminated, and the separators
 
145
    are canonical for the platform.
 
146
    
 
147
    The empty string as a dir name is taken as top-of-tree and matches 
 
148
    everything.
 
149
    
 
150
    >>> is_inside('src', 'src/foo.c')
 
151
    True
 
152
    >>> is_inside('src', 'srccontrol')
 
153
    False
 
154
    >>> is_inside('src', 'src/a/a/a/foo.c')
 
155
    True
 
156
    >>> is_inside('foo.c', 'foo.c')
 
157
    True
 
158
    >>> is_inside('foo.c', '')
 
159
    False
 
160
    >>> is_inside('', 'foo.c')
 
161
    True
93
162
    """
94
 
    return os.path.commonprefix([dir, fname]) == dir
 
163
    # XXX: Most callers of this can actually do something smarter by 
 
164
    # looking at the inventory
 
165
    if dir == fname:
 
166
        return True
 
167
    
 
168
    if dir == '':
 
169
        return True
 
170
    
 
171
    if dir[-1] != os.sep:
 
172
        dir += os.sep
 
173
    
 
174
    return fname.startswith(dir)
95
175
 
96
176
 
97
177
def is_inside_any(dir_list, fname):
98
178
    """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
179
    for dirname in dir_list:
104
180
        if is_inside(dirname, fname):
105
181
            return True
121
197
 
122
198
 
123
199
def sha_file(f):
124
 
    import sha
125
200
    if hasattr(f, 'tell'):
126
201
        assert f.tell() == 0
127
202
    s = sha.new()
134
209
    return s.hexdigest()
135
210
 
136
211
 
 
212
 
 
213
def sha_strings(strings):
 
214
    """Return the sha-1 of concatenation of strings"""
 
215
    s = sha.new()
 
216
    map(s.update, strings)
 
217
    return s.hexdigest()
 
218
 
 
219
 
137
220
def sha_string(f):
138
 
    import sha
139
221
    s = sha.new()
140
222
    s.update(f)
141
223
    return s.hexdigest()
143
225
 
144
226
 
145
227
def fingerprint_file(f):
146
 
    import sha
147
228
    s = sha.new()
148
229
    b = f.read()
149
230
    s.update(b)
198
279
    return realname, (username + '@' + socket.gethostname())
199
280
 
200
281
 
201
 
def _get_user_id():
 
282
def _get_user_id(branch):
202
283
    """Return the full user id from a file or environment variable.
203
284
 
204
 
    TODO: Allow taking this from a file in the branch directory too
205
 
    for per-branch ids."""
 
285
    e.g. "John Hacker <jhacker@foo.org>"
 
286
 
 
287
    branch
 
288
        A branch to use for a per-branch configuration, or None.
 
289
 
 
290
    The following are searched in order:
 
291
 
 
292
    1. $BZREMAIL
 
293
    2. .bzr/email for this branch.
 
294
    3. ~/.bzr.conf/email
 
295
    4. $EMAIL
 
296
    """
206
297
    v = os.environ.get('BZREMAIL')
207
298
    if v:
208
299
        return v.decode(bzrlib.user_encoding)
 
300
 
 
301
    if branch:
 
302
        try:
 
303
            return (branch.controlfile("email", "r") 
 
304
                    .read()
 
305
                    .decode(bzrlib.user_encoding)
 
306
                    .rstrip("\r\n"))
 
307
        except IOError, e:
 
308
            if e.errno != errno.ENOENT:
 
309
                raise
 
310
        except BzrError, e:
 
311
            pass
209
312
    
210
313
    try:
211
314
        return (open(os.path.join(config_dir(), "email"))
223
326
        return None
224
327
 
225
328
 
226
 
def username():
 
329
def username(branch):
227
330
    """Return email-style username.
228
331
 
229
332
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
230
333
 
231
334
    TODO: Check it's reasonably well-formed.
232
335
    """
233
 
    v = _get_user_id()
 
336
    v = _get_user_id(branch)
234
337
    if v:
235
338
        return v
236
339
    
241
344
        return email
242
345
 
243
346
 
244
 
_EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+')
245
 
def user_email():
 
347
def user_email(branch):
246
348
    """Return just the email component of a username."""
247
 
    e = _get_user_id()
 
349
    e = _get_user_id(branch)
248
350
    if e:
249
 
        m = _EMAIL_RE.search(e)
 
351
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
250
352
        if not m:
251
 
            bailout("%r doesn't seem to contain a reasonable email address" % e)
 
353
            raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
252
354
        return m.group(0)
253
355
 
254
356
    return _auto_user_id()[1]
296
398
        tt = time.localtime(t)
297
399
        offset = local_time_offset(t)
298
400
    else:
299
 
        bailout("unsupported timezone format %r",
300
 
                ['options are "utc", "original", "local"'])
 
401
        raise BzrError("unsupported timezone format %r" % timezone,
 
402
                       ['options are "utc", "original", "local"'])
301
403
 
302
404
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
303
405
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
345
447
    >>> splitpath('a/../b')
346
448
    Traceback (most recent call last):
347
449
    ...
348
 
    BzrError: ("sorry, '..' not allowed in path", [])
 
450
    BzrError: sorry, '..' not allowed in path
349
451
    """
350
452
    assert isinstance(p, types.StringTypes)
351
453
 
356
458
    rps = []
357
459
    for f in ps:
358
460
        if f == '..':
359
 
            bailout("sorry, %r not allowed in path" % f)
 
461
            raise BzrError("sorry, %r not allowed in path" % f)
360
462
        elif (f == '.') or (f == ''):
361
463
            pass
362
464
        else:
367
469
    assert isinstance(p, list)
368
470
    for f in p:
369
471
        if (f == '..') or (f == None) or (f == ''):
370
 
            bailout("sorry, %r not allowed in path" % f)
 
472
            raise BzrError("sorry, %r not allowed in path" % f)
371
473
    return os.path.join(*p)
372
474
 
373
475
 
382
484
    mutter('external command: %s' % `cmd`)
383
485
    if os.system(cmd):
384
486
        if not ignore_errors:
385
 
            bailout('command failed')
386
 
 
 
487
            raise BzrError('command failed')
 
488
 
 
489
 
 
490
def _read_config_value(name):
 
491
    """Read a config value from the file ~/.bzr.conf/<name>
 
492
    Return None if the file does not exist"""
 
493
    try:
 
494
        f = file(os.path.join(config_dir(), name), "r")
 
495
        return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
 
496
    except IOError, e:
 
497
        if e.errno == errno.ENOENT:
 
498
            return None
 
499
        raise
 
500
 
 
501
 
 
502
 
 
503
def split_lines(s):
 
504
    """Split s into lines, but without removing the newline characters."""
 
505
    return StringIO(s).readlines()
 
506