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 | ||
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 19 | import os, types, re, time, errno | 
| 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 | |
| 251
by mbp at sourcefrog - factor out locale.getpreferredencoding() | 24 | import bzrlib | 
| 1
by mbp at sourcefrog import from baz patch-364 | 25 | |
| 26 | def make_readonly(filename): | |
| 27 | """Make a filename read-only.""" | |
| 28 |     # TODO: probably needs to be fixed for windows
 | |
| 29 | mod = os.stat(filename).st_mode | |
| 30 | mod = mod & 0777555 | |
| 31 | os.chmod(filename, mod) | |
| 32 | ||
| 33 | ||
| 34 | def make_writable(filename): | |
| 35 | mod = os.stat(filename).st_mode | |
| 36 | mod = mod | 0200 | |
| 37 | os.chmod(filename, mod) | |
| 38 | ||
| 39 | ||
| 40 | _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])') | |
| 41 | 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 | |
| 48 | ||
| 49 | ||
| 50 | def file_kind(f): | |
| 51 | mode = os.lstat(f)[ST_MODE] | |
| 52 | if S_ISREG(mode): | |
| 53 | return 'file' | |
| 54 | elif S_ISDIR(mode): | |
| 55 | return 'directory' | |
| 20
by mbp at sourcefrog don't abort on trees that happen to contain symlinks | 56 | elif S_ISLNK(mode): | 
| 57 | return 'symlink' | |
| 1
by mbp at sourcefrog import from baz patch-364 | 58 | else: | 
| 183
by mbp at sourcefrog pychecker fixups | 59 | raise BzrError("can't handle file kind with mode %o of %r" % (mode, f)) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 60 | |
| 61 | ||
| 62 | ||
| 63 | def isdir(f): | |
| 64 | """True if f is an accessible directory.""" | |
| 65 | try: | |
| 66 | return S_ISDIR(os.lstat(f)[ST_MODE]) | |
| 67 | except OSError: | |
| 68 | return False | |
| 69 | ||
| 70 | ||
| 71 | ||
| 72 | def isfile(f): | |
| 73 | """True if f is a regular file.""" | |
| 74 | try: | |
| 75 | return S_ISREG(os.lstat(f)[ST_MODE]) | |
| 76 | except OSError: | |
| 77 | return False | |
| 78 | ||
| 79 | ||
| 80 | def pumpfile(fromfile, tofile): | |
| 81 | """Copy contents of one file to another.""" | |
| 82 | tofile.write(fromfile.read()) | |
| 83 | ||
| 84 | ||
| 85 | def uuid(): | |
| 86 | """Return a new UUID""" | |
| 63
by mbp at sourcefrog fix up uuid command | 87 | try: | 
| 319
by Martin Pool - remove trivial chomp() function | 88 | return file('/proc/sys/kernel/random/uuid').readline().rstrip('\n') | 
| 63
by mbp at sourcefrog fix up uuid command | 89 | except IOError: | 
| 90 | return chomp(os.popen('uuidgen').readline()) | |
| 91 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 92 | |
| 93 | def sha_file(f): | |
| 94 | import sha | |
| 95 | if hasattr(f, 'tell'): | |
| 96 | assert f.tell() == 0 | |
| 97 | s = sha.new() | |
| 320
by Martin Pool - Compute SHA-1 of files in chunks | 98 | BUFSIZE = 128<<10 | 
| 99 | while True: | |
| 100 | b = f.read(BUFSIZE) | |
| 101 | if not b: | |
| 102 |             break
 | |
| 103 | s.update(b) | |
| 1
by mbp at sourcefrog import from baz patch-364 | 104 | return s.hexdigest() | 
| 105 | ||
| 106 | ||
| 107 | def sha_string(f): | |
| 108 | import sha | |
| 109 | s = sha.new() | |
| 110 | s.update(f) | |
| 111 | return s.hexdigest() | |
| 112 | ||
| 113 | ||
| 114 | ||
| 124
by mbp at sourcefrog - check file text for past revisions is correct | 115 | def fingerprint_file(f): | 
| 116 | import sha | |
| 117 | s = sha.new() | |
| 126
by mbp at sourcefrog Use just one big read to fingerprint files | 118 | b = f.read() | 
| 119 | s.update(b) | |
| 120 | size = len(b) | |
| 124
by mbp at sourcefrog - check file text for past revisions is correct | 121 | return {'size': size, | 
| 122 | 'sha1': s.hexdigest()} | |
| 123 | ||
| 124 | ||
| 258
by Martin Pool - Take email from ~/.bzr.conf/email | 125 | def config_dir(): | 
| 126 | """Return per-user configuration directory. | |
| 127 | ||
| 128 |     By default this is ~/.bzr.conf/
 | |
| 129 |     
 | |
| 130 |     TODO: Global option --config-dir to override this.
 | |
| 131 |     """
 | |
| 132 | return os.path.expanduser("~/.bzr.conf") | |
| 133 | ||
| 134 | ||
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 135 | def _auto_user_id(): | 
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 136 | """Calculate automatic user identification. | 
| 137 | ||
| 138 |     Returns (realname, email).
 | |
| 139 | ||
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 140 |     Only used when none is set in the environment or the id file.
 | 
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 141 | |
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 142 |     This previously used the FQDN as the default domain, but that can
 | 
| 143 |     be very slow on machines where DNS is broken.  So now we simply
 | |
| 144 |     use the hostname.
 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 145 |     """
 | 
| 251
by mbp at sourcefrog - factor out locale.getpreferredencoding() | 146 | import socket | 
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 147 | |
| 148 |     # XXX: Any good way to get real user name on win32?
 | |
| 149 | ||
| 1
by mbp at sourcefrog import from baz patch-364 | 150 | try: | 
| 151 | import pwd | |
| 152 | uid = os.getuid() | |
| 153 | w = pwd.getpwuid(uid) | |
| 251
by mbp at sourcefrog - factor out locale.getpreferredencoding() | 154 | gecos = w.pw_gecos.decode(bzrlib.user_encoding) | 
| 155 | username = w.pw_name.decode(bzrlib.user_encoding) | |
| 25
by Martin Pool cope when gecos field doesn't have a comma | 156 | comma = gecos.find(',') | 
| 157 | if comma == -1: | |
| 158 | realname = gecos | |
| 159 | else: | |
| 160 | realname = gecos[:comma] | |
| 256
by Martin Pool - More handling of auto-username case | 161 | if not realname: | 
| 162 | realname = username | |
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 163 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 164 | except ImportError: | 
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 165 | import getpass | 
| 256
by Martin Pool - More handling of auto-username case | 166 | realname = username = getpass.getuser().decode(bzrlib.user_encoding) | 
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 167 | |
| 256
by Martin Pool - More handling of auto-username case | 168 | return realname, (username + '@' + socket.gethostname()) | 
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 169 | |
| 170 | ||
| 171 | def _get_user_id(): | |
| 258
by Martin Pool - Take email from ~/.bzr.conf/email | 172 | """Return the full user id from a file or environment variable. | 
| 173 | ||
| 174 |     TODO: Allow taking this from a file in the branch directory too
 | |
| 175 |     for per-branch ids."""
 | |
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 176 | v = os.environ.get('BZREMAIL') | 
| 177 | if v: | |
| 178 | return v.decode(bzrlib.user_encoding) | |
| 179 | ||
| 180 | try: | |
| 258
by Martin Pool - Take email from ~/.bzr.conf/email | 181 | return (open(os.path.join(config_dir(), "email")) | 
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 182 | .read() | 
| 183 | .decode(bzrlib.user_encoding) | |
| 184 | .rstrip("\r\n")) | |
| 256
by Martin Pool - More handling of auto-username case | 185 | except IOError, e: | 
| 186 | if e.errno != errno.ENOENT: | |
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 187 | raise e | 
| 188 | ||
| 189 | v = os.environ.get('EMAIL') | |
| 190 | if v: | |
| 191 | return v.decode(bzrlib.user_encoding) | |
| 192 | else: | |
| 193 | return None | |
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 194 | |
| 195 | ||
| 196 | def username(): | |
| 197 | """Return email-style username. | |
| 198 | ||
| 199 |     Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 | |
| 200 | ||
| 254
by Martin Pool - Doc cleanups from Magnus Therning | 201 |     TODO: Check it's reasonably well-formed.
 | 
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 202 |     """
 | 
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 203 | v = _get_user_id() | 
| 204 | if v: | |
| 205 | return v | |
| 206 | ||
| 207 | name, email = _auto_user_id() | |
| 246
by mbp at sourcefrog - unicode decoding in getting email and userid strings | 208 | if name: | 
| 209 | return '%s <%s>' % (name, email) | |
| 210 | else: | |
| 211 | return email | |
| 1
by mbp at sourcefrog import from baz patch-364 | 212 | |
| 213 | ||
| 183
by mbp at sourcefrog pychecker fixups | 214 | _EMAIL_RE = re.compile(r'[\w+.-]+@[\w+.-]+') | 
| 1
by mbp at sourcefrog import from baz patch-364 | 215 | def user_email(): | 
| 216 | """Return just the email component of a username.""" | |
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 217 | e = _get_user_id() | 
| 1
by mbp at sourcefrog import from baz patch-364 | 218 | if e: | 
| 183
by mbp at sourcefrog pychecker fixups | 219 | m = _EMAIL_RE.search(e) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 220 | if not m: | 
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 221 | bailout("%r doesn't seem to contain a reasonable email address" % e) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 222 | return m.group(0) | 
| 223 | ||
| 252
by Martin Pool - Don't use host fqdn for default user name, because DNS tends | 224 | return _auto_user_id()[1] | 
| 1
by mbp at sourcefrog import from baz patch-364 | 225 | |
| 226 | ||
| 227 | ||
| 228 | def compare_files(a, b): | |
| 229 | """Returns true if equal in contents""" | |
| 74
by mbp at sourcefrog compare_files: read in one page at a time rather than | 230 | BUFSIZE = 4096 | 
| 231 | while True: | |
| 232 | ai = a.read(BUFSIZE) | |
| 233 | bi = b.read(BUFSIZE) | |
| 234 | if ai != bi: | |
| 235 | return False | |
| 236 | if ai == '': | |
| 237 | return True | |
| 1
by mbp at sourcefrog import from baz patch-364 | 238 | |
| 239 | ||
| 240 | ||
| 49
by mbp at sourcefrog fix local-time-offset calculation | 241 | def local_time_offset(t=None): | 
| 242 | """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 | 243 |     # python2.3 localtime() can't take None
 | 
| 183
by mbp at sourcefrog pychecker fixups | 244 | if t == None: | 
| 73
by mbp at sourcefrog fix time.localtime call for python 2.3 | 245 | t = time.time() | 
| 246 | ||
| 49
by mbp at sourcefrog fix local-time-offset calculation | 247 | if time.localtime(t).tm_isdst and time.daylight: | 
| 8
by mbp at sourcefrog store committer's timezone in revision and show | 248 | return -time.altzone | 
| 249 | else: | |
| 250 | return -time.timezone | |
| 251 | ||
| 252 | ||
| 253 | def format_date(t, offset=0, timezone='original'): | |
| 1
by mbp at sourcefrog import from baz patch-364 | 254 |     ## TODO: Perhaps a global option to use either universal or local time?
 | 
| 255 |     ## Or perhaps just let people set $TZ?
 | |
| 256 | assert isinstance(t, float) | |
| 257 | ||
| 8
by mbp at sourcefrog store committer's timezone in revision and show | 258 | if timezone == 'utc': | 
| 1
by mbp at sourcefrog import from baz patch-364 | 259 | tt = time.gmtime(t) | 
| 260 | offset = 0 | |
| 8
by mbp at sourcefrog store committer's timezone in revision and show | 261 | elif timezone == 'original': | 
| 23
by mbp at sourcefrog format_date: handle revisions with no timezone offset | 262 | if offset == None: | 
| 263 | offset = 0 | |
| 16
by mbp at sourcefrog fix inverted calculation for original timezone -> utc | 264 | tt = time.gmtime(t + offset) | 
| 12
by mbp at sourcefrog new --timezone option for bzr log | 265 | elif timezone == 'local': | 
| 1
by mbp at sourcefrog import from baz patch-364 | 266 | tt = time.localtime(t) | 
| 49
by mbp at sourcefrog fix local-time-offset calculation | 267 | offset = local_time_offset(t) | 
| 12
by mbp at sourcefrog new --timezone option for bzr log | 268 | else: | 
| 269 | bailout("unsupported timezone format %r", | |
| 270 | ['options are "utc", "original", "local"']) | |
| 8
by mbp at sourcefrog store committer's timezone in revision and show | 271 | |
| 1
by mbp at sourcefrog import from baz patch-364 | 272 | return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt) | 
| 8
by mbp at sourcefrog store committer's timezone in revision and show | 273 | + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60)) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 274 | |
| 275 | ||
| 276 | def compact_date(when): | |
| 277 | return time.strftime('%Y%m%d%H%M%S', time.gmtime(when)) | |
| 278 | ||
| 279 | ||
| 280 | ||
| 281 | def filesize(f): | |
| 282 | """Return size of given open file.""" | |
| 283 | return os.fstat(f.fileno())[ST_SIZE] | |
| 284 | ||
| 285 | ||
| 286 | if hasattr(os, 'urandom'): # python 2.4 and later | |
| 287 | rand_bytes = os.urandom | |
| 288 | else: | |
| 289 |     # FIXME: No good on non-Linux
 | |
| 290 | _rand_file = file('/dev/urandom', 'rb') | |
| 291 | rand_bytes = _rand_file.read | |
| 292 | ||
| 293 | ||
| 294 | ## TODO: We could later have path objects that remember their list
 | |
| 295 | ## decomposition (might be too tricksy though.)
 | |
| 296 | ||
| 297 | def splitpath(p): | |
| 298 | """Turn string into list of parts. | |
| 299 | ||
| 300 |     >>> splitpath('a')
 | |
| 301 |     ['a']
 | |
| 302 |     >>> splitpath('a/b')
 | |
| 303 |     ['a', 'b']
 | |
| 304 |     >>> splitpath('a/./b')
 | |
| 305 |     ['a', 'b']
 | |
| 306 |     >>> splitpath('a/.b')
 | |
| 307 |     ['a', '.b']
 | |
| 308 |     >>> splitpath('a/../b')
 | |
| 184
by mbp at sourcefrog pychecker fixups | 309 |     Traceback (most recent call last):
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 310 |     ...
 | 
| 311 |     BzrError: ("sorry, '..' not allowed in path", [])
 | |
| 312 |     """
 | |
| 313 | assert isinstance(p, types.StringTypes) | |
| 271
by Martin Pool - Windows path fixes | 314 | |
| 315 |     # split on either delimiter because people might use either on
 | |
| 316 |     # Windows
 | |
| 317 | ps = re.split(r'[\\/]', p) | |
| 318 | ||
| 319 | rps = [] | |
| 1
by mbp at sourcefrog import from baz patch-364 | 320 | for f in ps: | 
| 321 | if f == '..': | |
| 322 | bailout("sorry, %r not allowed in path" % f) | |
| 271
by Martin Pool - Windows path fixes | 323 | elif (f == '.') or (f == ''): | 
| 324 |             pass
 | |
| 325 | else: | |
| 326 | rps.append(f) | |
| 327 | return rps | |
| 1
by mbp at sourcefrog import from baz patch-364 | 328 | |
| 329 | def joinpath(p): | |
| 330 | assert isinstance(p, list) | |
| 331 | for f in p: | |
| 183
by mbp at sourcefrog pychecker fixups | 332 | if (f == '..') or (f == None) or (f == ''): | 
| 1
by mbp at sourcefrog import from baz patch-364 | 333 | bailout("sorry, %r not allowed in path" % f) | 
| 271
by Martin Pool - Windows path fixes | 334 | return os.path.join(*p) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 335 | |
| 336 | ||
| 337 | def appendpath(p1, p2): | |
| 338 | if p1 == '': | |
| 339 | return p2 | |
| 340 | else: | |
| 271
by Martin Pool - Windows path fixes | 341 | return os.path.join(p1, p2) | 
| 1
by mbp at sourcefrog import from baz patch-364 | 342 | |
| 343 | ||
| 344 | def extern_command(cmd, ignore_errors = False): | |
| 345 | mutter('external command: %s' % `cmd`) | |
| 346 | if os.system(cmd): | |
| 347 | if not ignore_errors: | |
| 348 | bailout('command failed') | |
| 349 |