/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1 by mbp at sourcefrog
import from baz patch-364
1
# Bazaar-NG -- distributed version control
2
3
# Copyright (C) 2005 by Canonical Ltd
4
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
183 by mbp at sourcefrog
pychecker fixups
19
import os, types, re, time
20 by mbp at sourcefrog
don't abort on trees that happen to contain symlinks
20
from stat import S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE
1 by mbp at sourcefrog
import from baz patch-364
21
183 by mbp at sourcefrog
pychecker fixups
22
from errors import bailout, BzrError
23
from trace import mutter
1 by mbp at sourcefrog
import from baz patch-364
24
25
def make_readonly(filename):
26
    """Make a filename read-only."""
27
    # TODO: probably needs to be fixed for windows
28
    mod = os.stat(filename).st_mode
29
    mod = mod & 0777555
30
    os.chmod(filename, mod)
31
32
33
def make_writable(filename):
34
    mod = os.stat(filename).st_mode
35
    mod = mod | 0200
36
    os.chmod(filename, mod)
37
38
39
_QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
40
def quotefn(f):
41
    """Return shell-quoted filename"""
42
    ## We could be a bit more terse by using double-quotes etc
43
    f = _QUOTE_RE.sub(r'\\\1', f)
44
    if f[0] == '~':
45
        f[0:1] = r'\~' 
46
    return f
47
48
49
def file_kind(f):
50
    mode = os.lstat(f)[ST_MODE]
51
    if S_ISREG(mode):
52
        return 'file'
53
    elif S_ISDIR(mode):
54
        return 'directory'
20 by mbp at sourcefrog
don't abort on trees that happen to contain symlinks
55
    elif S_ISLNK(mode):
56
        return 'symlink'
1 by mbp at sourcefrog
import from baz patch-364
57
    else:
183 by mbp at sourcefrog
pychecker fixups
58
        raise BzrError("can't handle file kind with mode %o of %r" % (mode, f)) 
1 by mbp at sourcefrog
import from baz patch-364
59
60
61
62
def isdir(f):
63
    """True if f is an accessible directory."""
64
    try:
65
        return S_ISDIR(os.lstat(f)[ST_MODE])
66
    except OSError:
67
        return False
68
69
70
71
def isfile(f):
72
    """True if f is a regular file."""
73
    try:
74
        return S_ISREG(os.lstat(f)[ST_MODE])
75
    except OSError:
76
        return False
77
78
79
def pumpfile(fromfile, tofile):
80
    """Copy contents of one file to another."""
81
    tofile.write(fromfile.read())
82
83
84
def uuid():
85
    """Return a new UUID"""
86
    
87
    ## XXX: Could alternatively read /proc/sys/kernel/random/uuid on
88
    ## Linux, but we need something portable for other systems;
89
    ## preferably an implementation in Python.
63 by mbp at sourcefrog
fix up uuid command
90
    try:
91
        return chomp(file('/proc/sys/kernel/random/uuid').readline())
92
    except IOError:
93
        return chomp(os.popen('uuidgen').readline())
94
1 by mbp at sourcefrog
import from baz patch-364
95
96
def chomp(s):
97
    if s and (s[-1] == '\n'):
98
        return s[:-1]
99
    else:
100
        return s
101
102
103
def sha_file(f):
104
    import sha
105
    ## TODO: Maybe read in chunks to handle big files
106
    if hasattr(f, 'tell'):
107
        assert f.tell() == 0
108
    s = sha.new()
109
    s.update(f.read())
110
    return s.hexdigest()
111
112
113
def sha_string(f):
114
    import sha
115
    s = sha.new()
116
    s.update(f)
117
    return s.hexdigest()
118
119
120
124 by mbp at sourcefrog
- check file text for past revisions is correct
121
def fingerprint_file(f):
122
    import sha
123
    s = sha.new()
126 by mbp at sourcefrog
Use just one big read to fingerprint files
124
    b = f.read()
125
    s.update(b)
126
    size = len(b)
124 by mbp at sourcefrog
- check file text for past revisions is correct
127
    return {'size': size,
128
            'sha1': s.hexdigest()}
129
130
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
131
def auto_user_id():
132
    """Calculate automatic user identification.
133
134
    Returns (realname, email).
135
136
    Only used when none is set in the environment.
137
1 by mbp at sourcefrog
import from baz patch-364
138
    """
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
139
    import socket, locale
140
141
    # XXX: Any good way to get real user name on win32?
142
143
    # XXX: can the FQDN be non-ascii?
144
145
    enc = locale.getpreferredencoding()
1 by mbp at sourcefrog
import from baz patch-364
146
    
147
    try:
148
        import pwd
149
        uid = os.getuid()
150
        w = pwd.getpwuid(uid)
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
151
        gecos = w.pw_gecos.decode(enc)
152
        username = w.pw_name.decode(enc)
25 by Martin Pool
cope when gecos field doesn't have a comma
153
        comma = gecos.find(',')
154
        if comma == -1:
155
            realname = gecos
156
        else:
157
            realname = gecos[:comma]
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
158
159
        return realname, (username + '@' + socket.getfqdn())
160
1 by mbp at sourcefrog
import from baz patch-364
161
    except ImportError:
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
162
        import getpass
163
        return '', (getpass.getuser().decode(enc) + '@' + socket.getfqdn())
164
165
166
167
def username():
168
    """Return email-style username.
169
170
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
171
172
    :todo: Check it's reasonably well-formed.
173
174
    :todo: Allow taking it from a dotfile to help people on windows
175
           who can't easily set variables.
176
    """
177
    import locale
178
    e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
179
    if e:
180
        return e.decode(locale.getpreferredencoding())
181
182
    name, email = auto_user_id()
183
    if name:
184
        return '%s <%s>' % (name, email)
185
    else:
186
        return email
1 by mbp at sourcefrog
import from baz patch-364
187
188
183 by mbp at sourcefrog
pychecker fixups
189
_EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+')
1 by mbp at sourcefrog
import from baz patch-364
190
def user_email():
191
    """Return just the email component of a username."""
192
    e = os.environ.get('BZREMAIL') or os.environ.get('EMAIL')
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
193
    import locale
194
    e = e.decode(locale.getpreferredencoding())
1 by mbp at sourcefrog
import from baz patch-364
195
    if e:
183 by mbp at sourcefrog
pychecker fixups
196
        m = _EMAIL_RE.search(e)
1 by mbp at sourcefrog
import from baz patch-364
197
        if not m:
198
            bailout('%r is not a reasonable email address' % e)
199
        return m.group(0)
200
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
201
    return auto_user_id()[1]
1 by mbp at sourcefrog
import from baz patch-364
202
    
203
204
205
def compare_files(a, b):
206
    """Returns true if equal in contents"""
207
    # TODO: don't read the whole thing in one go.
74 by mbp at sourcefrog
compare_files: read in one page at a time rather than
208
    BUFSIZE = 4096
209
    while True:
210
        ai = a.read(BUFSIZE)
211
        bi = b.read(BUFSIZE)
212
        if ai != bi:
213
            return False
214
        if ai == '':
215
            return True
1 by mbp at sourcefrog
import from baz patch-364
216
217
218
49 by mbp at sourcefrog
fix local-time-offset calculation
219
def local_time_offset(t=None):
220
    """Return offset of local zone from GMT, either at present or at time t."""
73 by mbp at sourcefrog
fix time.localtime call for python 2.3
221
    # python2.3 localtime() can't take None
183 by mbp at sourcefrog
pychecker fixups
222
    if t == None:
73 by mbp at sourcefrog
fix time.localtime call for python 2.3
223
        t = time.time()
224
        
49 by mbp at sourcefrog
fix local-time-offset calculation
225
    if time.localtime(t).tm_isdst and time.daylight:
8 by mbp at sourcefrog
store committer's timezone in revision and show
226
        return -time.altzone
227
    else:
228
        return -time.timezone
229
230
    
231
def format_date(t, offset=0, timezone='original'):
1 by mbp at sourcefrog
import from baz patch-364
232
    ## TODO: Perhaps a global option to use either universal or local time?
233
    ## Or perhaps just let people set $TZ?
234
    assert isinstance(t, float)
235
    
8 by mbp at sourcefrog
store committer's timezone in revision and show
236
    if timezone == 'utc':
1 by mbp at sourcefrog
import from baz patch-364
237
        tt = time.gmtime(t)
238
        offset = 0
8 by mbp at sourcefrog
store committer's timezone in revision and show
239
    elif timezone == 'original':
23 by mbp at sourcefrog
format_date: handle revisions with no timezone offset
240
        if offset == None:
241
            offset = 0
16 by mbp at sourcefrog
fix inverted calculation for original timezone -> utc
242
        tt = time.gmtime(t + offset)
12 by mbp at sourcefrog
new --timezone option for bzr log
243
    elif timezone == 'local':
1 by mbp at sourcefrog
import from baz patch-364
244
        tt = time.localtime(t)
49 by mbp at sourcefrog
fix local-time-offset calculation
245
        offset = local_time_offset(t)
12 by mbp at sourcefrog
new --timezone option for bzr log
246
    else:
247
        bailout("unsupported timezone format %r",
248
                ['options are "utc", "original", "local"'])
8 by mbp at sourcefrog
store committer's timezone in revision and show
249
1 by mbp at sourcefrog
import from baz patch-364
250
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
8 by mbp at sourcefrog
store committer's timezone in revision and show
251
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
1 by mbp at sourcefrog
import from baz patch-364
252
253
254
def compact_date(when):
255
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
256
    
257
258
259
def filesize(f):
260
    """Return size of given open file."""
261
    return os.fstat(f.fileno())[ST_SIZE]
262
263
264
if hasattr(os, 'urandom'): # python 2.4 and later
265
    rand_bytes = os.urandom
266
else:
267
    # FIXME: No good on non-Linux
268
    _rand_file = file('/dev/urandom', 'rb')
269
    rand_bytes = _rand_file.read
270
271
272
## TODO: We could later have path objects that remember their list
273
## decomposition (might be too tricksy though.)
274
275
def splitpath(p):
276
    """Turn string into list of parts.
277
278
    >>> splitpath('a')
279
    ['a']
280
    >>> splitpath('a/b')
281
    ['a', 'b']
282
    >>> splitpath('a/./b')
283
    ['a', 'b']
284
    >>> splitpath('a/.b')
285
    ['a', '.b']
286
    >>> splitpath('a/../b')
184 by mbp at sourcefrog
pychecker fixups
287
    Traceback (most recent call last):
1 by mbp at sourcefrog
import from baz patch-364
288
    ...
289
    BzrError: ("sorry, '..' not allowed in path", [])
290
    """
291
    assert isinstance(p, types.StringTypes)
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
292
    ps = [f for f in p.split('/') if (f != '.' and f != '')]
1 by mbp at sourcefrog
import from baz patch-364
293
    for f in ps:
294
        if f == '..':
295
            bailout("sorry, %r not allowed in path" % f)
296
    return ps
297
298
def joinpath(p):
299
    assert isinstance(p, list)
300
    for f in p:
183 by mbp at sourcefrog
pychecker fixups
301
        if (f == '..') or (f == None) or (f == ''):
1 by mbp at sourcefrog
import from baz patch-364
302
            bailout("sorry, %r not allowed in path" % f)
303
    return '/'.join(p)
304
305
306
def appendpath(p1, p2):
307
    if p1 == '':
308
        return p2
309
    else:
310
        return p1 + '/' + p2
311
    
312
313
def extern_command(cmd, ignore_errors = False):
314
    mutter('external command: %s' % `cmd`)
315
    if os.system(cmd):
316
        if not ignore_errors:
317
            bailout('command failed')
318