##// END OF EJS Templates
move shortuser into util module.
Vadim Gelfer -
r1903:e4abeafd default
parent child Browse files
Show More
@@ -1,173 +1,167 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import os, ConfigParser
9 9 from i18n import gettext as _
10 10 from demandload import *
11 11 demandload(globals(), "re socket sys util")
12 12
13 13 class ui(object):
14 14 def __init__(self, verbose=False, debug=False, quiet=False,
15 15 interactive=True):
16 16 self.overlay = {}
17 17 self.cdata = ConfigParser.SafeConfigParser()
18 18 self.readconfig(util.rcpath)
19 19
20 20 self.quiet = self.configbool("ui", "quiet")
21 21 self.verbose = self.configbool("ui", "verbose")
22 22 self.debugflag = self.configbool("ui", "debug")
23 23 self.interactive = self.configbool("ui", "interactive", True)
24 24
25 25 self.updateopts(verbose, debug, quiet, interactive)
26 26 self.diffcache = None
27 27
28 28 def updateopts(self, verbose=False, debug=False, quiet=False,
29 29 interactive=True):
30 30 self.quiet = (self.quiet or quiet) and not verbose and not debug
31 31 self.verbose = (self.verbose or verbose) or debug
32 32 self.debugflag = (self.debugflag or debug)
33 33 self.interactive = (self.interactive and interactive)
34 34
35 35 def readconfig(self, fn):
36 36 if isinstance(fn, basestring):
37 37 fn = [fn]
38 38 for f in fn:
39 39 try:
40 40 self.cdata.read(f)
41 41 except ConfigParser.ParsingError, inst:
42 42 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
43 43
44 44 def setconfig(self, section, name, val):
45 45 self.overlay[(section, name)] = val
46 46
47 47 def config(self, section, name, default=None):
48 48 if self.overlay.has_key((section, name)):
49 49 return self.overlay[(section, name)]
50 50 if self.cdata.has_option(section, name):
51 51 return self.cdata.get(section, name)
52 52 return default
53 53
54 54 def configbool(self, section, name, default=False):
55 55 if self.overlay.has_key((section, name)):
56 56 return self.overlay[(section, name)]
57 57 if self.cdata.has_option(section, name):
58 58 return self.cdata.getboolean(section, name)
59 59 return default
60 60
61 61 def configitems(self, section):
62 62 if self.cdata.has_section(section):
63 63 return self.cdata.items(section)
64 64 return []
65 65
66 66 def walkconfig(self):
67 67 seen = {}
68 68 for (section, name), value in self.overlay.iteritems():
69 69 yield section, name, value
70 70 seen[section, name] = 1
71 71 for section in self.cdata.sections():
72 72 for name, value in self.cdata.items(section):
73 73 if (section, name) in seen: continue
74 74 yield section, name, value.replace('\n', '\\n')
75 75 seen[section, name] = 1
76 76
77 77 def extensions(self):
78 78 return self.configitems("extensions")
79 79
80 80 def diffopts(self):
81 81 if self.diffcache:
82 82 return self.diffcache
83 83 ret = { 'showfunc' : True, 'ignorews' : False}
84 84 for x in self.configitems("diff"):
85 85 k = x[0].lower()
86 86 v = x[1]
87 87 if v:
88 88 v = v.lower()
89 89 if v == 'true':
90 90 value = True
91 91 else:
92 92 value = False
93 93 ret[k] = value
94 94 self.diffcache = ret
95 95 return ret
96 96
97 97 def username(self):
98 98 return (os.environ.get("HGUSER") or
99 99 self.config("ui", "username") or
100 100 os.environ.get("EMAIL") or
101 101 (os.environ.get("LOGNAME",
102 102 os.environ.get("USERNAME", "unknown"))
103 103 + '@' + socket.getfqdn()))
104 104
105 105 def shortuser(self, user):
106 106 """Return a short representation of a user name or email address."""
107 if not self.verbose:
108 f = user.find('@')
109 if f >= 0:
110 user = user[:f]
111 f = user.find('<')
112 if f >= 0:
113 user = user[f+1:]
107 if not self.verbose: user = util.shortuser(user)
114 108 return user
115 109
116 110 def expandpath(self, loc, root=""):
117 111 paths = {}
118 112 for name, path in self.configitems("paths"):
119 113 m = path.find("://")
120 114 if m == -1:
121 115 path = os.path.join(root, path)
122 116 paths[name] = path
123 117
124 118 return paths.get(loc, loc)
125 119
126 120 def write(self, *args):
127 121 for a in args:
128 122 sys.stdout.write(str(a))
129 123
130 124 def write_err(self, *args):
131 125 if not sys.stdout.closed: sys.stdout.flush()
132 126 for a in args:
133 127 sys.stderr.write(str(a))
134 128
135 129 def readline(self):
136 130 return sys.stdin.readline()[:-1]
137 131 def prompt(self, msg, pat, default="y"):
138 132 if not self.interactive: return default
139 133 while 1:
140 134 self.write(msg, " ")
141 135 r = self.readline()
142 136 if re.match(pat, r):
143 137 return r
144 138 else:
145 139 self.write(_("unrecognized response\n"))
146 140 def status(self, *msg):
147 141 if not self.quiet: self.write(*msg)
148 142 def warn(self, *msg):
149 143 self.write_err(*msg)
150 144 def note(self, *msg):
151 145 if self.verbose: self.write(*msg)
152 146 def debug(self, *msg):
153 147 if self.debugflag: self.write(*msg)
154 148 def edit(self, text):
155 149 import tempfile
156 150 (fd, name) = tempfile.mkstemp("hg")
157 151 f = os.fdopen(fd, "w")
158 152 f.write(text)
159 153 f.close()
160 154
161 155 editor = (os.environ.get("HGEDITOR") or
162 156 self.config("ui", "editor") or
163 157 os.environ.get("EDITOR", "vi"))
164 158
165 159 os.environ["HGUSER"] = self.username()
166 160 util.system("%s \"%s\"" % (editor, name), errprefix=_("edit failed"))
167 161
168 162 t = open(name).read()
169 163 t = re.sub("(?m)^HG:.*\n", "", t)
170 164
171 165 os.unlink(name)
172 166
173 167 return t
@@ -1,692 +1,702 b''
1 1 """
2 2 util.py - Mercurial utility functions and platform specfic implementations
3 3
4 4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 5
6 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
8 8
9 9 This contains helper routines that are independent of the SCM core and hide
10 10 platform-specific details from the core.
11 11 """
12 12
13 13 import os, errno
14 14 from i18n import gettext as _
15 15 from demandload import *
16 16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
17 17 demandload(globals(), "threading time")
18 18
19 19 def pipefilter(s, cmd):
20 20 '''filter string S through command CMD, returning its output'''
21 21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
22 22 def writer():
23 23 pin.write(s)
24 24 pin.close()
25 25
26 26 # we should use select instead on UNIX, but this will work on most
27 27 # systems, including Windows
28 28 w = threading.Thread(target=writer)
29 29 w.start()
30 30 f = pout.read()
31 31 pout.close()
32 32 w.join()
33 33 return f
34 34
35 35 def tempfilter(s, cmd):
36 36 '''filter string S through a pair of temporary files with CMD.
37 37 CMD is used as a template to create the real command to be run,
38 38 with the strings INFILE and OUTFILE replaced by the real names of
39 39 the temporary files generated.'''
40 40 inname, outname = None, None
41 41 try:
42 42 infd, inname = tempfile.mkstemp(prefix='hgfin')
43 43 fp = os.fdopen(infd, 'wb')
44 44 fp.write(s)
45 45 fp.close()
46 46 outfd, outname = tempfile.mkstemp(prefix='hgfout')
47 47 os.close(outfd)
48 48 cmd = cmd.replace('INFILE', inname)
49 49 cmd = cmd.replace('OUTFILE', outname)
50 50 code = os.system(cmd)
51 51 if code: raise Abort(_("command '%s' failed: %s") %
52 52 (cmd, explain_exit(code)))
53 53 return open(outname, 'rb').read()
54 54 finally:
55 55 try:
56 56 if inname: os.unlink(inname)
57 57 except: pass
58 58 try:
59 59 if outname: os.unlink(outname)
60 60 except: pass
61 61
62 62 filtertable = {
63 63 'tempfile:': tempfilter,
64 64 'pipe:': pipefilter,
65 65 }
66 66
67 67 def filter(s, cmd):
68 68 "filter a string through a command that transforms its input to its output"
69 69 for name, fn in filtertable.iteritems():
70 70 if cmd.startswith(name):
71 71 return fn(s, cmd[len(name):].lstrip())
72 72 return pipefilter(s, cmd)
73 73
74 74 def patch(strip, patchname, ui):
75 75 """apply the patch <patchname> to the working directory.
76 76 a list of patched files is returned"""
77 77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
78 78 files = {}
79 79 for line in fp:
80 80 line = line.rstrip()
81 81 ui.status("%s\n" % line)
82 82 if line.startswith('patching file '):
83 83 pf = parse_patch_output(line)
84 84 files.setdefault(pf, 1)
85 85 code = fp.close()
86 86 if code:
87 87 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
88 88 return files.keys()
89 89
90 90 def binary(s):
91 91 """return true if a string is binary data using diff's heuristic"""
92 92 if s and '\0' in s[:4096]:
93 93 return True
94 94 return False
95 95
96 96 def unique(g):
97 97 """return the uniq elements of iterable g"""
98 98 seen = {}
99 99 for f in g:
100 100 if f not in seen:
101 101 seen[f] = 1
102 102 yield f
103 103
104 104 class Abort(Exception):
105 105 """Raised if a command needs to print an error and exit."""
106 106
107 107 def always(fn): return True
108 108 def never(fn): return False
109 109
110 110 def patkind(name, dflt_pat='glob'):
111 111 """Split a string into an optional pattern kind prefix and the
112 112 actual pattern."""
113 113 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
114 114 if name.startswith(prefix + ':'): return name.split(':', 1)
115 115 return dflt_pat, name
116 116
117 117 def globre(pat, head='^', tail='$'):
118 118 "convert a glob pattern into a regexp"
119 119 i, n = 0, len(pat)
120 120 res = ''
121 121 group = False
122 122 def peek(): return i < n and pat[i]
123 123 while i < n:
124 124 c = pat[i]
125 125 i = i+1
126 126 if c == '*':
127 127 if peek() == '*':
128 128 i += 1
129 129 res += '.*'
130 130 else:
131 131 res += '[^/]*'
132 132 elif c == '?':
133 133 res += '.'
134 134 elif c == '[':
135 135 j = i
136 136 if j < n and pat[j] in '!]':
137 137 j += 1
138 138 while j < n and pat[j] != ']':
139 139 j += 1
140 140 if j >= n:
141 141 res += '\\['
142 142 else:
143 143 stuff = pat[i:j].replace('\\','\\\\')
144 144 i = j + 1
145 145 if stuff[0] == '!':
146 146 stuff = '^' + stuff[1:]
147 147 elif stuff[0] == '^':
148 148 stuff = '\\' + stuff
149 149 res = '%s[%s]' % (res, stuff)
150 150 elif c == '{':
151 151 group = True
152 152 res += '(?:'
153 153 elif c == '}' and group:
154 154 res += ')'
155 155 group = False
156 156 elif c == ',' and group:
157 157 res += '|'
158 158 else:
159 159 res += re.escape(c)
160 160 return head + res + tail
161 161
162 162 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
163 163
164 164 def pathto(n1, n2):
165 165 '''return the relative path from one place to another.
166 166 this returns a path in the form used by the local filesystem, not hg.'''
167 167 if not n1: return localpath(n2)
168 168 a, b = n1.split('/'), n2.split('/')
169 169 a.reverse()
170 170 b.reverse()
171 171 while a and b and a[-1] == b[-1]:
172 172 a.pop()
173 173 b.pop()
174 174 b.reverse()
175 175 return os.sep.join((['..'] * len(a)) + b)
176 176
177 177 def canonpath(root, cwd, myname):
178 178 """return the canonical path of myname, given cwd and root"""
179 179 if root == os.sep:
180 180 rootsep = os.sep
181 181 else:
182 182 rootsep = root + os.sep
183 183 name = myname
184 184 if not name.startswith(os.sep):
185 185 name = os.path.join(root, cwd, name)
186 186 name = os.path.normpath(name)
187 187 if name.startswith(rootsep):
188 188 return pconvert(name[len(rootsep):])
189 189 elif name == root:
190 190 return ''
191 191 else:
192 192 raise Abort('%s not under root' % myname)
193 193
194 194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
195 195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
196 196
197 197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
198 198 if os.name == 'nt':
199 199 dflt_pat = 'glob'
200 200 else:
201 201 dflt_pat = 'relpath'
202 202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
203 203
204 204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
205 205 """build a function to match a set of file patterns
206 206
207 207 arguments:
208 208 canonroot - the canonical root of the tree you're matching against
209 209 cwd - the current working directory, if relevant
210 210 names - patterns to find
211 211 inc - patterns to include
212 212 exc - patterns to exclude
213 213 head - a regex to prepend to patterns to control whether a match is rooted
214 214
215 215 a pattern is one of:
216 216 'glob:<rooted glob>'
217 217 're:<rooted regexp>'
218 218 'path:<rooted path>'
219 219 'relglob:<relative glob>'
220 220 'relpath:<relative path>'
221 221 'relre:<relative regexp>'
222 222 '<rooted path or regexp>'
223 223
224 224 returns:
225 225 a 3-tuple containing
226 226 - list of explicit non-pattern names passed in
227 227 - a bool match(filename) function
228 228 - a bool indicating if any patterns were passed in
229 229
230 230 todo:
231 231 make head regex a rooted bool
232 232 """
233 233
234 234 def contains_glob(name):
235 235 for c in name:
236 236 if c in _globchars: return True
237 237 return False
238 238
239 239 def regex(kind, name, tail):
240 240 '''convert a pattern into a regular expression'''
241 241 if kind == 're':
242 242 return name
243 243 elif kind == 'path':
244 244 return '^' + re.escape(name) + '(?:/|$)'
245 245 elif kind == 'relglob':
246 246 return head + globre(name, '(?:|.*/)', tail)
247 247 elif kind == 'relpath':
248 248 return head + re.escape(name) + tail
249 249 elif kind == 'relre':
250 250 if name.startswith('^'):
251 251 return name
252 252 return '.*' + name
253 253 return head + globre(name, '', tail)
254 254
255 255 def matchfn(pats, tail):
256 256 """build a matching function from a set of patterns"""
257 257 if not pats:
258 258 return
259 259 matches = []
260 260 for k, p in pats:
261 261 try:
262 262 pat = '(?:%s)' % regex(k, p, tail)
263 263 matches.append(re.compile(pat).match)
264 264 except re.error:
265 265 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
266 266 else: raise Abort("invalid pattern (%s): %s" % (k, p))
267 267
268 268 def buildfn(text):
269 269 for m in matches:
270 270 r = m(text)
271 271 if r:
272 272 return r
273 273
274 274 return buildfn
275 275
276 276 def globprefix(pat):
277 277 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
278 278 root = []
279 279 for p in pat.split(os.sep):
280 280 if contains_glob(p): break
281 281 root.append(p)
282 282 return '/'.join(root)
283 283
284 284 pats = []
285 285 files = []
286 286 roots = []
287 287 for kind, name in [patkind(p, dflt_pat) for p in names]:
288 288 if kind in ('glob', 'relpath'):
289 289 name = canonpath(canonroot, cwd, name)
290 290 if name == '':
291 291 kind, name = 'glob', '**'
292 292 if kind in ('glob', 'path', 're'):
293 293 pats.append((kind, name))
294 294 if kind == 'glob':
295 295 root = globprefix(name)
296 296 if root: roots.append(root)
297 297 elif kind == 'relpath':
298 298 files.append((kind, name))
299 299 roots.append(name)
300 300
301 301 patmatch = matchfn(pats, '$') or always
302 302 filematch = matchfn(files, '(?:/|$)') or always
303 303 incmatch = always
304 304 if inc:
305 305 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
306 306 excmatch = lambda fn: False
307 307 if exc:
308 308 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
309 309
310 310 return (roots,
311 311 lambda fn: (incmatch(fn) and not excmatch(fn) and
312 312 (fn.endswith('/') or
313 313 (not pats and not files) or
314 314 (pats and patmatch(fn)) or
315 315 (files and filematch(fn)))),
316 316 (inc or exc or (pats and pats != [('glob', '**')])) and True)
317 317
318 318 def system(cmd, errprefix=None):
319 319 """execute a shell command that must succeed"""
320 320 rc = os.system(cmd)
321 321 if rc:
322 322 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
323 323 explain_exit(rc)[0])
324 324 if errprefix:
325 325 errmsg = "%s: %s" % (errprefix, errmsg)
326 326 raise Abort(errmsg)
327 327
328 328 def rename(src, dst):
329 329 """forcibly rename a file"""
330 330 try:
331 331 os.rename(src, dst)
332 332 except:
333 333 os.unlink(dst)
334 334 os.rename(src, dst)
335 335
336 336 def unlink(f):
337 337 """unlink and remove the directory if it is empty"""
338 338 os.unlink(f)
339 339 # try removing directories that might now be empty
340 340 try: os.removedirs(os.path.dirname(f))
341 341 except: pass
342 342
343 343 def copyfiles(src, dst, hardlink=None):
344 344 """Copy a directory tree using hardlinks if possible"""
345 345
346 346 if hardlink is None:
347 347 hardlink = (os.stat(src).st_dev ==
348 348 os.stat(os.path.dirname(dst)).st_dev)
349 349
350 350 if os.path.isdir(src):
351 351 os.mkdir(dst)
352 352 for name in os.listdir(src):
353 353 srcname = os.path.join(src, name)
354 354 dstname = os.path.join(dst, name)
355 355 copyfiles(srcname, dstname, hardlink)
356 356 else:
357 357 if hardlink:
358 358 try:
359 359 os_link(src, dst)
360 360 except:
361 361 hardlink = False
362 362 shutil.copy(src, dst)
363 363 else:
364 364 shutil.copy(src, dst)
365 365
366 366 def opener(base):
367 367 """
368 368 return a function that opens files relative to base
369 369
370 370 this function is used to hide the details of COW semantics and
371 371 remote file access from higher level code.
372 372 """
373 373 p = base
374 374
375 375 def mktempcopy(name):
376 376 d, fn = os.path.split(name)
377 377 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
378 378 fp = os.fdopen(fd, "wb")
379 379 try:
380 380 fp.write(file(name, "rb").read())
381 381 except:
382 382 try: os.unlink(temp)
383 383 except: pass
384 384 raise
385 385 fp.close()
386 386 st = os.lstat(name)
387 387 os.chmod(temp, st.st_mode)
388 388 return temp
389 389
390 390 class atomicfile(file):
391 391 """the file will only be copied on close"""
392 392 def __init__(self, name, mode, atomic=False):
393 393 self.__name = name
394 394 self.temp = mktempcopy(name)
395 395 file.__init__(self, self.temp, mode)
396 396 def close(self):
397 397 if not self.closed:
398 398 file.close(self)
399 399 rename(self.temp, self.__name)
400 400 def __del__(self):
401 401 self.close()
402 402
403 403 def o(path, mode="r", text=False, atomic=False):
404 404 f = os.path.join(p, path)
405 405
406 406 if not text:
407 407 mode += "b" # for that other OS
408 408
409 409 if mode[0] != "r":
410 410 try:
411 411 nlink = nlinks(f)
412 412 except OSError:
413 413 d = os.path.dirname(f)
414 414 if not os.path.isdir(d):
415 415 os.makedirs(d)
416 416 else:
417 417 if atomic:
418 418 return atomicfile(f, mode)
419 419 if nlink > 1:
420 420 rename(mktempcopy(f), f)
421 421 return file(f, mode)
422 422
423 423 return o
424 424
425 425 def _makelock_file(info, pathname):
426 426 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
427 427 os.write(ld, info)
428 428 os.close(ld)
429 429
430 430 def _readlock_file(pathname):
431 431 return file(pathname).read()
432 432
433 433 def nlinks(pathname):
434 434 """Return number of hardlinks for the given file."""
435 435 return os.stat(pathname).st_nlink
436 436
437 437 if hasattr(os, 'link'):
438 438 os_link = os.link
439 439 else:
440 440 def os_link(src, dst):
441 441 raise OSError(0, _("Hardlinks not supported"))
442 442
443 443 # Platform specific variants
444 444 if os.name == 'nt':
445 445 demandload(globals(), "msvcrt")
446 446 nulldev = 'NUL:'
447 447
448 448 class winstdout:
449 449 '''stdout on windows misbehaves if sent through a pipe'''
450 450
451 451 def __init__(self, fp):
452 452 self.fp = fp
453 453
454 454 def __getattr__(self, key):
455 455 return getattr(self.fp, key)
456 456
457 457 def close(self):
458 458 try:
459 459 self.fp.close()
460 460 except: pass
461 461
462 462 def write(self, s):
463 463 try:
464 464 return self.fp.write(s)
465 465 except IOError, inst:
466 466 if inst.errno != 0: raise
467 467 self.close()
468 468 raise IOError(errno.EPIPE, 'Broken pipe')
469 469
470 470 sys.stdout = winstdout(sys.stdout)
471 471
472 472 try:
473 473 import win32api, win32process
474 474 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
475 475 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
476 476
477 477 except ImportError:
478 478 systemrc = r'c:\mercurial\mercurial.ini'
479 479 pass
480 480
481 481 rcpath = (systemrc,
482 482 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
483 483
484 484 def parse_patch_output(output_line):
485 485 """parses the output produced by patch and returns the file name"""
486 486 pf = output_line[14:]
487 487 if pf[0] == '`':
488 488 pf = pf[1:-1] # Remove the quotes
489 489 return pf
490 490
491 491 try: # ActivePython can create hard links using win32file module
492 492 import win32file
493 493
494 494 def os_link(src, dst): # NB will only succeed on NTFS
495 495 win32file.CreateHardLink(dst, src)
496 496
497 497 def nlinks(pathname):
498 498 """Return number of hardlinks for the given file."""
499 499 try:
500 500 fh = win32file.CreateFile(pathname,
501 501 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
502 502 None, win32file.OPEN_EXISTING, 0, None)
503 503 res = win32file.GetFileInformationByHandle(fh)
504 504 fh.Close()
505 505 return res[7]
506 506 except:
507 507 return os.stat(pathname).st_nlink
508 508
509 509 except ImportError:
510 510 pass
511 511
512 512 def is_exec(f, last):
513 513 return last
514 514
515 515 def set_exec(f, mode):
516 516 pass
517 517
518 518 def set_binary(fd):
519 519 msvcrt.setmode(fd.fileno(), os.O_BINARY)
520 520
521 521 def pconvert(path):
522 522 return path.replace("\\", "/")
523 523
524 524 def localpath(path):
525 525 return path.replace('/', '\\')
526 526
527 527 def normpath(path):
528 528 return pconvert(os.path.normpath(path))
529 529
530 530 makelock = _makelock_file
531 531 readlock = _readlock_file
532 532
533 533 def explain_exit(code):
534 534 return _("exited with status %d") % code, code
535 535
536 536 else:
537 537 nulldev = '/dev/null'
538 538
539 539 def rcfiles(path):
540 540 rcs = [os.path.join(path, 'hgrc')]
541 541 rcdir = os.path.join(path, 'hgrc.d')
542 542 try:
543 543 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
544 544 if f.endswith(".rc")])
545 545 except OSError, inst: pass
546 546 return rcs
547 547 rcpath = []
548 548 if len(sys.argv) > 0:
549 549 rcpath.extend(rcfiles(os.path.dirname(sys.argv[0]) + '/../etc/mercurial'))
550 550 rcpath.extend(rcfiles('/etc/mercurial'))
551 551 rcpath.append(os.path.expanduser('~/.hgrc'))
552 552 rcpath = [os.path.normpath(f) for f in rcpath]
553 553
554 554 def parse_patch_output(output_line):
555 555 """parses the output produced by patch and returns the file name"""
556 556 pf = output_line[14:]
557 557 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
558 558 pf = pf[1:-1] # Remove the quotes
559 559 return pf
560 560
561 561 def is_exec(f, last):
562 562 """check whether a file is executable"""
563 563 return (os.stat(f).st_mode & 0100 != 0)
564 564
565 565 def set_exec(f, mode):
566 566 s = os.stat(f).st_mode
567 567 if (s & 0100 != 0) == mode:
568 568 return
569 569 if mode:
570 570 # Turn on +x for every +r bit when making a file executable
571 571 # and obey umask.
572 572 umask = os.umask(0)
573 573 os.umask(umask)
574 574 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
575 575 else:
576 576 os.chmod(f, s & 0666)
577 577
578 578 def set_binary(fd):
579 579 pass
580 580
581 581 def pconvert(path):
582 582 return path
583 583
584 584 def localpath(path):
585 585 return path
586 586
587 587 normpath = os.path.normpath
588 588
589 589 def makelock(info, pathname):
590 590 try:
591 591 os.symlink(info, pathname)
592 592 except OSError, why:
593 593 if why.errno == errno.EEXIST:
594 594 raise
595 595 else:
596 596 _makelock_file(info, pathname)
597 597
598 598 def readlock(pathname):
599 599 try:
600 600 return os.readlink(pathname)
601 601 except OSError, why:
602 602 if why.errno == errno.EINVAL:
603 603 return _readlock_file(pathname)
604 604 else:
605 605 raise
606 606
607 607 def explain_exit(code):
608 608 """return a 2-tuple (desc, code) describing a process's status"""
609 609 if os.WIFEXITED(code):
610 610 val = os.WEXITSTATUS(code)
611 611 return _("exited with status %d") % val, val
612 612 elif os.WIFSIGNALED(code):
613 613 val = os.WTERMSIG(code)
614 614 return _("killed by signal %d") % val, val
615 615 elif os.WIFSTOPPED(code):
616 616 val = os.WSTOPSIG(code)
617 617 return _("stopped by signal %d") % val, val
618 618 raise ValueError(_("invalid exit code"))
619 619
620 620 class chunkbuffer(object):
621 621 """Allow arbitrary sized chunks of data to be efficiently read from an
622 622 iterator over chunks of arbitrary size."""
623 623
624 624 def __init__(self, in_iter, targetsize = 2**16):
625 625 """in_iter is the iterator that's iterating over the input chunks.
626 626 targetsize is how big a buffer to try to maintain."""
627 627 self.in_iter = iter(in_iter)
628 628 self.buf = ''
629 629 self.targetsize = int(targetsize)
630 630 if self.targetsize <= 0:
631 631 raise ValueError(_("targetsize must be greater than 0, was %d") %
632 632 targetsize)
633 633 self.iterempty = False
634 634
635 635 def fillbuf(self):
636 636 """Ignore target size; read every chunk from iterator until empty."""
637 637 if not self.iterempty:
638 638 collector = cStringIO.StringIO()
639 639 collector.write(self.buf)
640 640 for ch in self.in_iter:
641 641 collector.write(ch)
642 642 self.buf = collector.getvalue()
643 643 self.iterempty = True
644 644
645 645 def read(self, l):
646 646 """Read L bytes of data from the iterator of chunks of data.
647 647 Returns less than L bytes if the iterator runs dry."""
648 648 if l > len(self.buf) and not self.iterempty:
649 649 # Clamp to a multiple of self.targetsize
650 650 targetsize = self.targetsize * ((l // self.targetsize) + 1)
651 651 collector = cStringIO.StringIO()
652 652 collector.write(self.buf)
653 653 collected = len(self.buf)
654 654 for chunk in self.in_iter:
655 655 collector.write(chunk)
656 656 collected += len(chunk)
657 657 if collected >= targetsize:
658 658 break
659 659 if collected < targetsize:
660 660 self.iterempty = True
661 661 self.buf = collector.getvalue()
662 662 s, self.buf = self.buf[:l], buffer(self.buf, l)
663 663 return s
664 664
665 665 def filechunkiter(f, size = 65536):
666 666 """Create a generator that produces all the data in the file size
667 667 (default 65536) bytes at a time. Chunks may be less than size
668 668 bytes if the chunk is the last chunk in the file, or the file is a
669 669 socket or some other type of file that sometimes reads less data
670 670 than is requested."""
671 671 s = f.read(size)
672 672 while len(s) > 0:
673 673 yield s
674 674 s = f.read(size)
675 675
676 676 def makedate():
677 677 lt = time.localtime()
678 678 if lt[8] == 1 and time.daylight:
679 679 tz = time.altzone
680 680 else:
681 681 tz = time.timezone
682 682 return time.mktime(lt), tz
683 683
684 684 def datestr(date=None, format='%c'):
685 685 """represent a (unixtime, offset) tuple as a localized time.
686 686 unixtime is seconds since the epoch, and offset is the time zone's
687 687 number of seconds away from UTC."""
688 688 t, tz = date or makedate()
689 689 return ("%s %+03d%02d" %
690 690 (time.strftime(format, time.gmtime(float(t) - tz)),
691 691 -tz / 3600,
692 692 ((-tz % 3600) / 60)))
693
694 def shortuser(user):
695 """Return a short representation of a user name or email address."""
696 f = user.find('@')
697 if f >= 0:
698 user = user[:f]
699 f = user.find('<')
700 if f >= 0:
701 user = user[f+1:]
702 return user
General Comments 0
You need to be logged in to leave comments. Login now