~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-23 06:25:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050323062555-5489339018d0c043
- import a subset of elementtree for easier installation

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, errno, sys
 
19
import os, types, re, time, types
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
24
 
import bzrlib
 
22
from errors import bailout
25
23
 
26
24
def make_readonly(filename):
27
25
    """Make a filename read-only."""
56
54
    elif S_ISLNK(mode):
57
55
        return 'symlink'
58
56
    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)
 
57
        bailout("can't handle file kind with mode %o of %r" % (mode, f)) 
71
58
 
72
59
 
73
60
 
88
75
        return False
89
76
 
90
77
 
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
 
 
110
78
def pumpfile(fromfile, tofile):
111
79
    """Copy contents of one file to another."""
112
80
    tofile.write(fromfile.read())
114
82
 
115
83
def uuid():
116
84
    """Return a new UUID"""
 
85
    
 
86
    ## XXX: Could alternatively read /proc/sys/kernel/random/uuid on
 
87
    ## Linux, but we need something portable for other systems;
 
88
    ## preferably an implementation in Python.
117
89
    try:
118
 
        return file('/proc/sys/kernel/random/uuid').readline().rstrip('\n')
 
90
        return chomp(file('/proc/sys/kernel/random/uuid').readline())
119
91
    except IOError:
120
92
        return chomp(os.popen('uuidgen').readline())
121
93
 
122
94
 
 
95
def chomp(s):
 
96
    if s and (s[-1] == '\n'):
 
97
        return s[:-1]
 
98
    else:
 
99
        return s
 
100
 
 
101
 
123
102
def sha_file(f):
124
103
    import sha
 
104
    ## TODO: Maybe read in chunks to handle big files
125
105
    if hasattr(f, 'tell'):
126
106
        assert f.tell() == 0
127
107
    s = sha.new()
128
 
    BUFSIZE = 128<<10
129
 
    while True:
130
 
        b = f.read(BUFSIZE)
131
 
        if not b:
132
 
            break
133
 
        s.update(b)
 
108
    s.update(f.read())
134
109
    return s.hexdigest()
135
110
 
136
111
 
142
117
 
143
118
 
144
119
 
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
 
    """
 
120
def username():
 
121
    """Return email-style username.
 
122
 
 
123
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
124
 
 
125
    :todo: Check it's reasonably well-formed.
 
126
 
 
127
    :todo: Allow taking it from a dotfile to help people on windows
 
128
           who can't easily set variables.
 
129
 
 
130
    :todo: Cope without pwd module, which is only on unix. 
 
131
    """
 
132
    e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
 
133
    if e: return e
 
134
 
176
135
    import socket
177
 
 
178
 
    # XXX: Any good way to get real user name on win32?
179
 
 
 
136
    
180
137
    try:
181
138
        import pwd
182
139
        uid = os.getuid()
183
140
        w = pwd.getpwuid(uid)
184
 
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
185
 
        username = w.pw_name.decode(bzrlib.user_encoding)
 
141
        gecos = w.pw_gecos
186
142
        comma = gecos.find(',')
187
143
        if comma == -1:
188
144
            realname = gecos
189
145
        else:
190
146
            realname = gecos[:comma]
191
 
        if not realname:
192
 
            realname = username
193
 
 
 
147
        return '%s <%s@%s>' % (realname, w.pw_name, socket.getfqdn())
194
148
    except ImportError:
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+.-]+')
 
149
        pass
 
150
 
 
151
    import getpass, socket
 
152
    return '<%s@%s>' % (getpass.getuser(), socket.getfqdn())
 
153
 
 
154
 
245
155
def user_email():
246
156
    """Return just the email component of a username."""
247
 
    e = _get_user_id()
 
157
    e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
248
158
    if e:
249
 
        m = _EMAIL_RE.search(e)
 
159
        import re
 
160
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
250
161
        if not m:
251
 
            bailout("%r doesn't seem to contain a reasonable email address" % e)
 
162
            bailout('%r is not a reasonable email address' % e)
252
163
        return m.group(0)
253
164
 
254
 
    return _auto_user_id()[1]
 
165
 
 
166
    import getpass, socket
 
167
    return '%s@%s' % (getpass.getuser(), socket.getfqdn())
 
168
 
255
169
    
256
170
 
257
171
 
258
172
def compare_files(a, b):
259
173
    """Returns true if equal in contents"""
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
 
174
    # TODO: don't read the whole thing in one go.
 
175
    result = a.read() == b.read()
 
176
    return result
268
177
 
269
178
 
270
179
 
271
180
def local_time_offset(t=None):
272
181
    """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
182
    if time.localtime(t).tm_isdst and time.daylight:
278
183
        return -time.altzone
279
184
    else:
283
188
def format_date(t, offset=0, timezone='original'):
284
189
    ## TODO: Perhaps a global option to use either universal or local time?
285
190
    ## Or perhaps just let people set $TZ?
 
191
    import time
 
192
    
286
193
    assert isinstance(t, float)
287
194
    
288
195
    if timezone == 'utc':
315
222
 
316
223
if hasattr(os, 'urandom'): # python 2.4 and later
317
224
    rand_bytes = os.urandom
318
 
elif sys.platform == 'linux2':
319
 
    rand_bytes = file('/dev/urandom', 'rb').read
320
225
else:
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
 
226
    # FIXME: No good on non-Linux
 
227
    _rand_file = file('/dev/urandom', 'rb')
 
228
    rand_bytes = _rand_file.read
329
229
 
330
230
 
331
231
## TODO: We could later have path objects that remember their list
348
248
    BzrError: ("sorry, '..' not allowed in path", [])
349
249
    """
350
250
    assert isinstance(p, types.StringTypes)
351
 
 
352
 
    # split on either delimiter because people might use either on
353
 
    # Windows
354
 
    ps = re.split(r'[\\/]', p)
355
 
 
356
 
    rps = []
 
251
    ps = [f for f in p.split('/') if f != '.']
357
252
    for f in ps:
358
253
        if f == '..':
359
254
            bailout("sorry, %r not allowed in path" % f)
360
 
        elif (f == '.') or (f == ''):
361
 
            pass
362
 
        else:
363
 
            rps.append(f)
364
 
    return rps
 
255
    return ps
365
256
 
366
257
def joinpath(p):
367
258
    assert isinstance(p, list)
368
259
    for f in p:
369
 
        if (f == '..') or (f == None) or (f == ''):
 
260
        if (f == '..') or (f is None) or (f == ''):
370
261
            bailout("sorry, %r not allowed in path" % f)
371
 
    return os.path.join(*p)
 
262
    return '/'.join(p)
372
263
 
373
264
 
374
265
def appendpath(p1, p2):
375
266
    if p1 == '':
376
267
        return p2
377
268
    else:
378
 
        return os.path.join(p1, p2)
 
269
        return p1 + '/' + p2
379
270
    
380
271
 
381
272
def extern_command(cmd, ignore_errors = False):