~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: Martin Pool
  • Date: 2005-05-19 08:31:06 UTC
  • Revision ID: mbp@sourcefrog.net-20050519083106-ebe71562d3bda4a7
- fix typo

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
 
import os, types, re, time, types
20
 
from stat import S_ISREG, S_ISDIR, ST_MODE, ST_SIZE
 
19
import os, types, re, time, errno, sys
 
20
from stat import S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE
21
21
 
22
 
from errors import bailout
 
22
from errors import bailout, BzrError
 
23
from trace import mutter
 
24
import bzrlib
23
25
 
24
26
def make_readonly(filename):
25
27
    """Make a filename read-only."""
51
53
        return 'file'
52
54
    elif S_ISDIR(mode):
53
55
        return 'directory'
54
 
    else:
55
 
        bailout("can't handle file kind of %r" % fp)
 
56
    elif S_ISLNK(mode):
 
57
        return 'symlink'
 
58
    else:
 
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)
56
71
 
57
72
 
58
73
 
73
88
        return False
74
89
 
75
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
 
76
110
def pumpfile(fromfile, tofile):
77
111
    """Copy contents of one file to another."""
78
112
    tofile.write(fromfile.read())
80
114
 
81
115
def uuid():
82
116
    """Return a new UUID"""
83
 
    
84
 
    ## XXX: Could alternatively read /proc/sys/kernel/random/uuid on
85
 
    ## Linux, but we need something portable for other systems;
86
 
    ## preferably an implementation in Python.
87
 
    bailout('uuids not allowed!')
88
 
    return chomp(os.popen('uuidgen').readline())
89
 
 
90
 
def chomp(s):
91
 
    if s and (s[-1] == '\n'):
92
 
        return s[:-1]
93
 
    else:
94
 
        return s
 
117
    try:
 
118
        return file('/proc/sys/kernel/random/uuid').readline().rstrip('\n')
 
119
    except IOError:
 
120
        return chomp(os.popen('uuidgen').readline())
95
121
 
96
122
 
97
123
def sha_file(f):
98
124
    import sha
99
 
    ## TODO: Maybe read in chunks to handle big files
100
125
    if hasattr(f, 'tell'):
101
126
        assert f.tell() == 0
102
127
    s = sha.new()
103
 
    s.update(f.read())
 
128
    BUFSIZE = 128<<10
 
129
    while True:
 
130
        b = f.read(BUFSIZE)
 
131
        if not b:
 
132
            break
 
133
        s.update(b)
104
134
    return s.hexdigest()
105
135
 
106
136
 
112
142
 
113
143
 
114
144
 
115
 
def username():
116
 
    """Return email-style username.
117
 
 
118
 
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
119
 
 
120
 
    :todo: Check it's reasonably well-formed.
121
 
 
122
 
    :todo: Allow taking it from a dotfile to help people on windows
123
 
           who can't easily set variables.
124
 
 
125
 
    :todo: Cope without pwd module, which is only on unix. 
126
 
    """
127
 
    e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
128
 
    if e: return e
129
 
 
 
145
def fingerprint_file(f):
 
146
    import sha
 
147
    s = sha.new()
 
148
    b = f.read()
 
149
    s.update(b)
 
150
    size = len(b)
 
151
    return {'size': size,
 
152
            'sha1': s.hexdigest()}
 
153
 
 
154
 
 
155
def config_dir():
 
156
    """Return per-user configuration directory.
 
157
 
 
158
    By default this is ~/.bzr.conf/
 
159
    
 
160
    TODO: Global option --config-dir to override this.
 
161
    """
 
162
    return os.path.expanduser("~/.bzr.conf")
 
163
 
 
164
 
 
165
def _auto_user_id():
 
166
    """Calculate automatic user identification.
 
167
 
 
168
    Returns (realname, email).
 
169
 
 
170
    Only used when none is set in the environment or the id file.
 
171
 
 
172
    This previously used the FQDN as the default domain, but that can
 
173
    be very slow on machines where DNS is broken.  So now we simply
 
174
    use the hostname.
 
175
    """
130
176
    import socket
131
 
    
 
177
 
 
178
    # XXX: Any good way to get real user name on win32?
 
179
 
132
180
    try:
133
181
        import pwd
134
182
        uid = os.getuid()
135
183
        w = pwd.getpwuid(uid)
136
 
        realname, junk = w.pw_gecos.split(',', 1)
137
 
        return '%s <%s@%s>' % (realname, w.pw_name, socket.getfqdn())
 
184
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
185
        username = w.pw_name.decode(bzrlib.user_encoding)
 
186
        comma = gecos.find(',')
 
187
        if comma == -1:
 
188
            realname = gecos
 
189
        else:
 
190
            realname = gecos[:comma]
 
191
        if not realname:
 
192
            realname = username
 
193
 
138
194
    except ImportError:
139
 
        pass
140
 
 
141
 
    import getpass, socket
142
 
    return '<%s@%s>' % (getpass.getuser(), socket.getfqdn())
143
 
 
144
 
 
 
195
        import getpass
 
196
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
197
 
 
198
    return realname, (username + '@' + socket.gethostname())
 
199
 
 
200
 
 
201
def _get_user_id():
 
202
    """Return the full user id from a file or environment variable.
 
203
 
 
204
    TODO: Allow taking this from a file in the branch directory too
 
205
    for per-branch ids."""
 
206
    v = os.environ.get('BZREMAIL')
 
207
    if v:
 
208
        return v.decode(bzrlib.user_encoding)
 
209
    
 
210
    try:
 
211
        return (open(os.path.join(config_dir(), "email"))
 
212
                .read()
 
213
                .decode(bzrlib.user_encoding)
 
214
                .rstrip("\r\n"))
 
215
    except IOError, e:
 
216
        if e.errno != errno.ENOENT:
 
217
            raise e
 
218
 
 
219
    v = os.environ.get('EMAIL')
 
220
    if v:
 
221
        return v.decode(bzrlib.user_encoding)
 
222
    else:    
 
223
        return None
 
224
 
 
225
 
 
226
def username():
 
227
    """Return email-style username.
 
228
 
 
229
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
230
 
 
231
    TODO: Check it's reasonably well-formed.
 
232
    """
 
233
    v = _get_user_id()
 
234
    if v:
 
235
        return v
 
236
    
 
237
    name, email = _auto_user_id()
 
238
    if name:
 
239
        return '%s <%s>' % (name, email)
 
240
    else:
 
241
        return email
 
242
 
 
243
 
 
244
_EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+')
145
245
def user_email():
146
246
    """Return just the email component of a username."""
147
 
    e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
 
247
    e = _get_user_id()
148
248
    if e:
149
 
        import re
150
 
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
249
        m = _EMAIL_RE.search(e)
151
250
        if not m:
152
 
            bailout('%r is not a reasonable email address' % e)
 
251
            bailout("%r doesn't seem to contain a reasonable email address" % e)
153
252
        return m.group(0)
154
253
 
155
 
 
156
 
    import getpass, socket
157
 
    return '%s@%s' % (getpass.getuser(), socket.getfqdn())
158
 
 
 
254
    return _auto_user_id()[1]
159
255
    
160
256
 
161
257
 
162
258
def compare_files(a, b):
163
259
    """Returns true if equal in contents"""
164
 
    # TODO: don't read the whole thing in one go.
165
 
    result = a.read() == b.read()
166
 
    return result
167
 
 
168
 
 
169
 
 
170
 
def local_time_offset():
171
 
    if time.daylight:
 
260
    BUFSIZE = 4096
 
261
    while True:
 
262
        ai = a.read(BUFSIZE)
 
263
        bi = b.read(BUFSIZE)
 
264
        if ai != bi:
 
265
            return False
 
266
        if ai == '':
 
267
            return True
 
268
 
 
269
 
 
270
 
 
271
def local_time_offset(t=None):
 
272
    """Return offset of local zone from GMT, either at present or at time t."""
 
273
    # python2.3 localtime() can't take None
 
274
    if t == None:
 
275
        t = time.time()
 
276
        
 
277
    if time.localtime(t).tm_isdst and time.daylight:
172
278
        return -time.altzone
173
279
    else:
174
280
        return -time.timezone
177
283
def format_date(t, offset=0, timezone='original'):
178
284
    ## TODO: Perhaps a global option to use either universal or local time?
179
285
    ## Or perhaps just let people set $TZ?
180
 
    import time
181
 
    
182
286
    assert isinstance(t, float)
183
287
    
184
288
    if timezone == 'utc':
185
289
        tt = time.gmtime(t)
186
290
        offset = 0
187
291
    elif timezone == 'original':
188
 
        tt = time.gmtime(t - offset)
 
292
        if offset == None:
 
293
            offset = 0
 
294
        tt = time.gmtime(t + offset)
189
295
    elif timezone == 'local':
190
296
        tt = time.localtime(t)
191
 
        offset = local_time_offset()
 
297
        offset = local_time_offset(t)
192
298
    else:
193
299
        bailout("unsupported timezone format %r",
194
300
                ['options are "utc", "original", "local"'])
209
315
 
210
316
if hasattr(os, 'urandom'): # python 2.4 and later
211
317
    rand_bytes = os.urandom
 
318
elif sys.platform == 'linux2':
 
319
    rand_bytes = file('/dev/urandom', 'rb').read
212
320
else:
213
 
    # FIXME: No good on non-Linux
214
 
    _rand_file = file('/dev/urandom', 'rb')
215
 
    rand_bytes = _rand_file.read
 
321
    # not well seeded, but better than nothing
 
322
    def rand_bytes(n):
 
323
        import random
 
324
        s = ''
 
325
        while n:
 
326
            s += chr(random.randint(0, 255))
 
327
            n -= 1
 
328
        return s
216
329
 
217
330
 
218
331
## TODO: We could later have path objects that remember their list
235
348
    BzrError: ("sorry, '..' not allowed in path", [])
236
349
    """
237
350
    assert isinstance(p, types.StringTypes)
238
 
    ps = [f for f in p.split('/') if f != '.']
 
351
 
 
352
    # split on either delimiter because people might use either on
 
353
    # Windows
 
354
    ps = re.split(r'[\\/]', p)
 
355
 
 
356
    rps = []
239
357
    for f in ps:
240
358
        if f == '..':
241
359
            bailout("sorry, %r not allowed in path" % f)
242
 
    return ps
 
360
        elif (f == '.') or (f == ''):
 
361
            pass
 
362
        else:
 
363
            rps.append(f)
 
364
    return rps
243
365
 
244
366
def joinpath(p):
245
367
    assert isinstance(p, list)
246
368
    for f in p:
247
 
        if (f == '..') or (f is None) or (f == ''):
 
369
        if (f == '..') or (f == None) or (f == ''):
248
370
            bailout("sorry, %r not allowed in path" % f)
249
 
    return '/'.join(p)
 
371
    return os.path.join(*p)
250
372
 
251
373
 
252
374
def appendpath(p1, p2):
253
375
    if p1 == '':
254
376
        return p2
255
377
    else:
256
 
        return p1 + '/' + p2
 
378
        return os.path.join(p1, p2)
257
379
    
258
380
 
259
381
def extern_command(cmd, ignore_errors = False):