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
19
import os, types, re, time, types
20
from stat import S_ISREG, S_ISDIR, S_ISLNK, 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
S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
22
from errors import bailout
23
from bzrlib.errors import BzrError
24
from bzrlib.trace import mutter
24
27
def make_readonly(filename):
25
28
"""Make a filename read-only."""
35
38
os.chmod(filename, mod)
38
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
40
"""Return shell-quoted filename"""
41
## We could be a bit more terse by using double-quotes etc
42
f = _QUOTE_RE.sub(r'\\\1', f)
45
"""Return a quoted filename filename
47
This previously used backslash quoting, but that works poorly on
49
# TODO: I'm not really sure this is the best format either.x
52
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
54
if _QUOTE_RE.search(f):
54
66
elif S_ISLNK(mode):
57
bailout("can't handle file kind with mode %o of %r" % (mode, f))
80
def kind_marker(kind):
83
elif kind == 'directory':
85
elif kind == 'symlink':
88
raise BzrError('invalid file kind %r' % kind)
93
"""Copy a file to a backup.
95
Backups are named in GNU-style, with a ~ suffix.
97
If the file is already a backup, it's not copied.
110
outf = file(bfn, 'wb')
116
def rename(path_from, path_to):
117
"""Basically the same as os.rename() just special for win32"""
118
if sys.platform == 'win32':
122
if e.errno != e.ENOENT:
124
os.rename(path_from, path_to)
147
def is_inside(dir, fname):
148
"""True if fname is inside dir.
150
The parameters should typically be passed to os.path.normpath first, so
151
that . and .. and repeated slashes are eliminated, and the separators
152
are canonical for the platform.
154
The empty string as a dir name is taken as top-of-tree and matches
157
>>> is_inside('src', 'src/foo.c')
159
>>> is_inside('src', 'srccontrol')
161
>>> is_inside('src', 'src/a/a/a/foo.c')
163
>>> is_inside('foo.c', 'foo.c')
165
>>> is_inside('foo.c', '')
167
>>> is_inside('', 'foo.c')
170
# XXX: Most callers of this can actually do something smarter by
171
# looking at the inventory
178
if dir[-1] != os.sep:
181
return fname.startswith(dir)
184
def is_inside_any(dir_list, fname):
185
"""True if fname is inside any of given dirs."""
186
for dirname in dir_list:
187
if is_inside(dirname, fname):
78
193
def pumpfile(fromfile, tofile):
79
194
"""Copy contents of one file to another."""
80
195
tofile.write(fromfile.read())
118
"""Return email-style username.
120
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
122
:todo: Check it's reasonably well-formed.
124
:todo: Allow taking it from a dotfile to help people on windows
125
who can't easily set variables.
127
:todo: Cope without pwd module, which is only on unix.
129
e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
228
def fingerprint_file(f):
234
return {'size': size,
235
'sha1': s.hexdigest()}
239
"""Return per-user configuration directory.
241
By default this is ~/.bzr.conf/
243
TODO: Global option --config-dir to override this.
245
return os.path.expanduser("~/.bzr.conf")
249
"""Calculate automatic user identification.
251
Returns (realname, email).
253
Only used when none is set in the environment or the id file.
255
This previously used the FQDN as the default domain, but that can
256
be very slow on machines where DNS is broken. So now we simply
261
# XXX: Any good way to get real user name on win32?
136
265
uid = os.getuid()
137
266
w = pwd.getpwuid(uid)
267
gecos = w.pw_gecos.decode(bzrlib.user_encoding)
268
username = w.pw_name.decode(bzrlib.user_encoding)
139
269
comma = gecos.find(',')
143
273
realname = gecos[:comma]
144
return '%s <%s@%s>' % (realname, w.pw_name, socket.getfqdn())
145
277
except ImportError:
148
import getpass, socket
149
return '<%s@%s>' % (getpass.getuser(), socket.getfqdn())
279
realname = username = getpass.getuser().decode(bzrlib.user_encoding)
281
return realname, (username + '@' + socket.gethostname())
284
def _get_user_id(branch):
285
"""Return the full user id from a file or environment variable.
287
e.g. "John Hacker <jhacker@foo.org>"
290
A branch to use for a per-branch configuration, or None.
292
The following are searched in order:
295
2. .bzr/email for this branch.
299
v = os.environ.get('BZREMAIL')
301
return v.decode(bzrlib.user_encoding)
305
return (branch.controlfile("email", "r")
307
.decode(bzrlib.user_encoding)
310
if e.errno != errno.ENOENT:
316
return (open(os.path.join(config_dir(), "email"))
318
.decode(bzrlib.user_encoding)
321
if e.errno != errno.ENOENT:
324
v = os.environ.get('EMAIL')
326
return v.decode(bzrlib.user_encoding)
331
def username(branch):
332
"""Return email-style username.
334
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
336
TODO: Check it's reasonably well-formed.
338
v = _get_user_id(branch)
342
name, email = _auto_user_id()
344
return '%s <%s>' % (name, email)
349
def user_email(branch):
153
350
"""Return just the email component of a username."""
154
e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
351
e = _get_user_id(branch)
157
353
m = re.search(r'[\w+.-]+@[\w+.-]+', e)
159
bailout('%r is not a reasonable email address' % e)
355
raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
160
356
return m.group(0)
163
import getpass, socket
164
return '%s@%s' % (getpass.getuser(), socket.getfqdn())
358
return _auto_user_id()[1]
169
362
def compare_files(a, b):
170
363
"""Returns true if equal in contents"""
171
# TODO: don't read the whole thing in one go.
172
result = a.read() == b.read()
177
def local_time_offset():
375
def local_time_offset(t=None):
376
"""Return offset of local zone from GMT, either at present or at time t."""
377
# python2.3 localtime() can't take None
381
if time.localtime(t).tm_isdst and time.daylight:
179
382
return -time.altzone
181
384
return -time.timezone
197
398
tt = time.gmtime(t + offset)
198
399
elif timezone == 'local':
199
400
tt = time.localtime(t)
200
offset = local_time_offset()
401
offset = local_time_offset(t)
202
bailout("unsupported timezone format %r",
203
['options are "utc", "original", "local"'])
403
raise BzrError("unsupported timezone format %r" % timezone,
404
['options are "utc", "original", "local"'])
205
406
return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
206
407
+ ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
215
416
"""Return size of given open file."""
216
417
return os.fstat(f.fileno())[ST_SIZE]
219
if hasattr(os, 'urandom'): # python 2.4 and later
419
# Define rand_bytes based on platform.
421
# Python 2.4 and later have os.urandom,
422
# but it doesn't work on some arches
220
424
rand_bytes = os.urandom
222
# FIXME: No good on non-Linux
223
_rand_file = file('/dev/urandom', 'rb')
224
rand_bytes = _rand_file.read
425
except (NotImplementedError, AttributeError):
426
# If python doesn't have os.urandom, or it doesn't work,
427
# then try to first pull random data from /dev/urandom
428
if os.path.exists("/dev/urandom"):
429
rand_bytes = file('/dev/urandom', 'rb').read
430
# Otherwise, use this hack as a last resort
432
# not well seeded, but better than nothing
437
s += chr(random.randint(0, 255))
227
441
## TODO: We could later have path objects that remember their list
228
442
## decomposition (might be too tricksy though.)
241
455
>>> splitpath('a/../b')
242
456
Traceback (most recent call last):
244
BzrError: ("sorry, '..' not allowed in path", [])
458
BzrError: sorry, '..' not allowed in path
246
460
assert isinstance(p, types.StringTypes)
247
ps = [f for f in p.split('/') if f != '.']
462
# split on either delimiter because people might use either on
464
ps = re.split(r'[\\/]', p)
250
bailout("sorry, %r not allowed in path" % f)
469
raise BzrError("sorry, %r not allowed in path" % f)
470
elif (f == '.') or (f == ''):
254
477
assert isinstance(p, list)
256
if (f == '..') or (f is None) or (f == ''):
257
bailout("sorry, %r not allowed in path" % f)
479
if (f == '..') or (f == None) or (f == ''):
480
raise BzrError("sorry, %r not allowed in path" % f)
481
return os.path.join(*p)
261
484
def appendpath(p1, p2):
488
return os.path.join(p1, p2)
268
491
def extern_command(cmd, ignore_errors = False):
269
492
mutter('external command: %s' % `cmd`)
270
493
if os.system(cmd):
271
494
if not ignore_errors:
272
bailout('command failed')
495
raise BzrError('command failed')
498
def _read_config_value(name):
499
"""Read a config value from the file ~/.bzr.conf/<name>
500
Return None if the file does not exist"""
502
f = file(os.path.join(config_dir(), name), "r")
503
return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
505
if e.errno == errno.ENOENT: