##// END OF EJS Templates
Workaround for "Not enough space" error due to the bug of Windows....
Shun-ichi GOTO -
r5647:165cda75 default
parent child Browse files
Show More
@@ -1,1742 +1,1750 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import re, urlparse
18 import re, urlparse
19
19
20 try:
20 try:
21 set = set
21 set = set
22 frozenset = frozenset
22 frozenset = frozenset
23 except NameError:
23 except NameError:
24 from sets import Set as set, ImmutableSet as frozenset
24 from sets import Set as set, ImmutableSet as frozenset
25
25
26 try:
26 try:
27 _encoding = os.environ.get("HGENCODING")
27 _encoding = os.environ.get("HGENCODING")
28 if sys.platform == 'darwin' and not _encoding:
28 if sys.platform == 'darwin' and not _encoding:
29 # On darwin, getpreferredencoding ignores the locale environment and
29 # On darwin, getpreferredencoding ignores the locale environment and
30 # always returns mac-roman. We override this if the environment is
30 # always returns mac-roman. We override this if the environment is
31 # not C (has been customized by the user).
31 # not C (has been customized by the user).
32 locale.setlocale(locale.LC_CTYPE, '')
32 locale.setlocale(locale.LC_CTYPE, '')
33 _encoding = locale.getlocale()[1]
33 _encoding = locale.getlocale()[1]
34 if not _encoding:
34 if not _encoding:
35 _encoding = locale.getpreferredencoding() or 'ascii'
35 _encoding = locale.getpreferredencoding() or 'ascii'
36 except locale.Error:
36 except locale.Error:
37 _encoding = 'ascii'
37 _encoding = 'ascii'
38 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
38 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
39 _fallbackencoding = 'ISO-8859-1'
39 _fallbackencoding = 'ISO-8859-1'
40
40
41 def tolocal(s):
41 def tolocal(s):
42 """
42 """
43 Convert a string from internal UTF-8 to local encoding
43 Convert a string from internal UTF-8 to local encoding
44
44
45 All internal strings should be UTF-8 but some repos before the
45 All internal strings should be UTF-8 but some repos before the
46 implementation of locale support may contain latin1 or possibly
46 implementation of locale support may contain latin1 or possibly
47 other character sets. We attempt to decode everything strictly
47 other character sets. We attempt to decode everything strictly
48 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
48 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
49 replace unknown characters.
49 replace unknown characters.
50 """
50 """
51 for e in ('UTF-8', _fallbackencoding):
51 for e in ('UTF-8', _fallbackencoding):
52 try:
52 try:
53 u = s.decode(e) # attempt strict decoding
53 u = s.decode(e) # attempt strict decoding
54 return u.encode(_encoding, "replace")
54 return u.encode(_encoding, "replace")
55 except LookupError, k:
55 except LookupError, k:
56 raise Abort(_("%s, please check your locale settings") % k)
56 raise Abort(_("%s, please check your locale settings") % k)
57 except UnicodeDecodeError:
57 except UnicodeDecodeError:
58 pass
58 pass
59 u = s.decode("utf-8", "replace") # last ditch
59 u = s.decode("utf-8", "replace") # last ditch
60 return u.encode(_encoding, "replace")
60 return u.encode(_encoding, "replace")
61
61
62 def fromlocal(s):
62 def fromlocal(s):
63 """
63 """
64 Convert a string from the local character encoding to UTF-8
64 Convert a string from the local character encoding to UTF-8
65
65
66 We attempt to decode strings using the encoding mode set by
66 We attempt to decode strings using the encoding mode set by
67 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
67 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
68 characters will cause an error message. Other modes include
68 characters will cause an error message. Other modes include
69 'replace', which replaces unknown characters with a special
69 'replace', which replaces unknown characters with a special
70 Unicode character, and 'ignore', which drops the character.
70 Unicode character, and 'ignore', which drops the character.
71 """
71 """
72 try:
72 try:
73 return s.decode(_encoding, _encodingmode).encode("utf-8")
73 return s.decode(_encoding, _encodingmode).encode("utf-8")
74 except UnicodeDecodeError, inst:
74 except UnicodeDecodeError, inst:
75 sub = s[max(0, inst.start-10):inst.start+10]
75 sub = s[max(0, inst.start-10):inst.start+10]
76 raise Abort("decoding near '%s': %s!" % (sub, inst))
76 raise Abort("decoding near '%s': %s!" % (sub, inst))
77 except LookupError, k:
77 except LookupError, k:
78 raise Abort(_("%s, please check your locale settings") % k)
78 raise Abort(_("%s, please check your locale settings") % k)
79
79
80 def locallen(s):
80 def locallen(s):
81 """Find the length in characters of a local string"""
81 """Find the length in characters of a local string"""
82 return len(s.decode(_encoding, "replace"))
82 return len(s.decode(_encoding, "replace"))
83
83
84 def localsub(s, a, b=None):
84 def localsub(s, a, b=None):
85 try:
85 try:
86 u = s.decode(_encoding, _encodingmode)
86 u = s.decode(_encoding, _encodingmode)
87 if b is not None:
87 if b is not None:
88 u = u[a:b]
88 u = u[a:b]
89 else:
89 else:
90 u = u[:a]
90 u = u[:a]
91 return u.encode(_encoding, _encodingmode)
91 return u.encode(_encoding, _encodingmode)
92 except UnicodeDecodeError, inst:
92 except UnicodeDecodeError, inst:
93 sub = s[max(0, inst.start-10), inst.start+10]
93 sub = s[max(0, inst.start-10), inst.start+10]
94 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
94 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
95
95
96 # used by parsedate
96 # used by parsedate
97 defaultdateformats = (
97 defaultdateformats = (
98 '%Y-%m-%d %H:%M:%S',
98 '%Y-%m-%d %H:%M:%S',
99 '%Y-%m-%d %I:%M:%S%p',
99 '%Y-%m-%d %I:%M:%S%p',
100 '%Y-%m-%d %H:%M',
100 '%Y-%m-%d %H:%M',
101 '%Y-%m-%d %I:%M%p',
101 '%Y-%m-%d %I:%M%p',
102 '%Y-%m-%d',
102 '%Y-%m-%d',
103 '%m-%d',
103 '%m-%d',
104 '%m/%d',
104 '%m/%d',
105 '%m/%d/%y',
105 '%m/%d/%y',
106 '%m/%d/%Y',
106 '%m/%d/%Y',
107 '%a %b %d %H:%M:%S %Y',
107 '%a %b %d %H:%M:%S %Y',
108 '%a %b %d %I:%M:%S%p %Y',
108 '%a %b %d %I:%M:%S%p %Y',
109 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
109 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
110 '%b %d %H:%M:%S %Y',
110 '%b %d %H:%M:%S %Y',
111 '%b %d %I:%M:%S%p %Y',
111 '%b %d %I:%M:%S%p %Y',
112 '%b %d %H:%M:%S',
112 '%b %d %H:%M:%S',
113 '%b %d %I:%M:%S%p',
113 '%b %d %I:%M:%S%p',
114 '%b %d %H:%M',
114 '%b %d %H:%M',
115 '%b %d %I:%M%p',
115 '%b %d %I:%M%p',
116 '%b %d %Y',
116 '%b %d %Y',
117 '%b %d',
117 '%b %d',
118 '%H:%M:%S',
118 '%H:%M:%S',
119 '%I:%M:%SP',
119 '%I:%M:%SP',
120 '%H:%M',
120 '%H:%M',
121 '%I:%M%p',
121 '%I:%M%p',
122 )
122 )
123
123
124 extendeddateformats = defaultdateformats + (
124 extendeddateformats = defaultdateformats + (
125 "%Y",
125 "%Y",
126 "%Y-%m",
126 "%Y-%m",
127 "%b",
127 "%b",
128 "%b %Y",
128 "%b %Y",
129 )
129 )
130
130
131 class SignalInterrupt(Exception):
131 class SignalInterrupt(Exception):
132 """Exception raised on SIGTERM and SIGHUP."""
132 """Exception raised on SIGTERM and SIGHUP."""
133
133
134 # differences from SafeConfigParser:
134 # differences from SafeConfigParser:
135 # - case-sensitive keys
135 # - case-sensitive keys
136 # - allows values that are not strings (this means that you may not
136 # - allows values that are not strings (this means that you may not
137 # be able to save the configuration to a file)
137 # be able to save the configuration to a file)
138 class configparser(ConfigParser.SafeConfigParser):
138 class configparser(ConfigParser.SafeConfigParser):
139 def optionxform(self, optionstr):
139 def optionxform(self, optionstr):
140 return optionstr
140 return optionstr
141
141
142 def set(self, section, option, value):
142 def set(self, section, option, value):
143 return ConfigParser.ConfigParser.set(self, section, option, value)
143 return ConfigParser.ConfigParser.set(self, section, option, value)
144
144
145 def _interpolate(self, section, option, rawval, vars):
145 def _interpolate(self, section, option, rawval, vars):
146 if not isinstance(rawval, basestring):
146 if not isinstance(rawval, basestring):
147 return rawval
147 return rawval
148 return ConfigParser.SafeConfigParser._interpolate(self, section,
148 return ConfigParser.SafeConfigParser._interpolate(self, section,
149 option, rawval, vars)
149 option, rawval, vars)
150
150
151 def cachefunc(func):
151 def cachefunc(func):
152 '''cache the result of function calls'''
152 '''cache the result of function calls'''
153 # XXX doesn't handle keywords args
153 # XXX doesn't handle keywords args
154 cache = {}
154 cache = {}
155 if func.func_code.co_argcount == 1:
155 if func.func_code.co_argcount == 1:
156 # we gain a small amount of time because
156 # we gain a small amount of time because
157 # we don't need to pack/unpack the list
157 # we don't need to pack/unpack the list
158 def f(arg):
158 def f(arg):
159 if arg not in cache:
159 if arg not in cache:
160 cache[arg] = func(arg)
160 cache[arg] = func(arg)
161 return cache[arg]
161 return cache[arg]
162 else:
162 else:
163 def f(*args):
163 def f(*args):
164 if args not in cache:
164 if args not in cache:
165 cache[args] = func(*args)
165 cache[args] = func(*args)
166 return cache[args]
166 return cache[args]
167
167
168 return f
168 return f
169
169
170 def pipefilter(s, cmd):
170 def pipefilter(s, cmd):
171 '''filter string S through command CMD, returning its output'''
171 '''filter string S through command CMD, returning its output'''
172 (pin, pout) = os.popen2(cmd, 'b')
172 (pin, pout) = os.popen2(cmd, 'b')
173 def writer():
173 def writer():
174 try:
174 try:
175 pin.write(s)
175 pin.write(s)
176 pin.close()
176 pin.close()
177 except IOError, inst:
177 except IOError, inst:
178 if inst.errno != errno.EPIPE:
178 if inst.errno != errno.EPIPE:
179 raise
179 raise
180
180
181 # we should use select instead on UNIX, but this will work on most
181 # we should use select instead on UNIX, but this will work on most
182 # systems, including Windows
182 # systems, including Windows
183 w = threading.Thread(target=writer)
183 w = threading.Thread(target=writer)
184 w.start()
184 w.start()
185 f = pout.read()
185 f = pout.read()
186 pout.close()
186 pout.close()
187 w.join()
187 w.join()
188 return f
188 return f
189
189
190 def tempfilter(s, cmd):
190 def tempfilter(s, cmd):
191 '''filter string S through a pair of temporary files with CMD.
191 '''filter string S through a pair of temporary files with CMD.
192 CMD is used as a template to create the real command to be run,
192 CMD is used as a template to create the real command to be run,
193 with the strings INFILE and OUTFILE replaced by the real names of
193 with the strings INFILE and OUTFILE replaced by the real names of
194 the temporary files generated.'''
194 the temporary files generated.'''
195 inname, outname = None, None
195 inname, outname = None, None
196 try:
196 try:
197 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
197 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
198 fp = os.fdopen(infd, 'wb')
198 fp = os.fdopen(infd, 'wb')
199 fp.write(s)
199 fp.write(s)
200 fp.close()
200 fp.close()
201 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
201 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
202 os.close(outfd)
202 os.close(outfd)
203 cmd = cmd.replace('INFILE', inname)
203 cmd = cmd.replace('INFILE', inname)
204 cmd = cmd.replace('OUTFILE', outname)
204 cmd = cmd.replace('OUTFILE', outname)
205 code = os.system(cmd)
205 code = os.system(cmd)
206 if sys.platform == 'OpenVMS' and code & 1:
206 if sys.platform == 'OpenVMS' and code & 1:
207 code = 0
207 code = 0
208 if code: raise Abort(_("command '%s' failed: %s") %
208 if code: raise Abort(_("command '%s' failed: %s") %
209 (cmd, explain_exit(code)))
209 (cmd, explain_exit(code)))
210 return open(outname, 'rb').read()
210 return open(outname, 'rb').read()
211 finally:
211 finally:
212 try:
212 try:
213 if inname: os.unlink(inname)
213 if inname: os.unlink(inname)
214 except: pass
214 except: pass
215 try:
215 try:
216 if outname: os.unlink(outname)
216 if outname: os.unlink(outname)
217 except: pass
217 except: pass
218
218
219 filtertable = {
219 filtertable = {
220 'tempfile:': tempfilter,
220 'tempfile:': tempfilter,
221 'pipe:': pipefilter,
221 'pipe:': pipefilter,
222 }
222 }
223
223
224 def filter(s, cmd):
224 def filter(s, cmd):
225 "filter a string through a command that transforms its input to its output"
225 "filter a string through a command that transforms its input to its output"
226 for name, fn in filtertable.iteritems():
226 for name, fn in filtertable.iteritems():
227 if cmd.startswith(name):
227 if cmd.startswith(name):
228 return fn(s, cmd[len(name):].lstrip())
228 return fn(s, cmd[len(name):].lstrip())
229 return pipefilter(s, cmd)
229 return pipefilter(s, cmd)
230
230
231 def binary(s):
231 def binary(s):
232 """return true if a string is binary data using diff's heuristic"""
232 """return true if a string is binary data using diff's heuristic"""
233 if s and '\0' in s[:4096]:
233 if s and '\0' in s[:4096]:
234 return True
234 return True
235 return False
235 return False
236
236
237 def unique(g):
237 def unique(g):
238 """return the uniq elements of iterable g"""
238 """return the uniq elements of iterable g"""
239 seen = {}
239 seen = {}
240 l = []
240 l = []
241 for f in g:
241 for f in g:
242 if f not in seen:
242 if f not in seen:
243 seen[f] = 1
243 seen[f] = 1
244 l.append(f)
244 l.append(f)
245 return l
245 return l
246
246
247 class Abort(Exception):
247 class Abort(Exception):
248 """Raised if a command needs to print an error and exit."""
248 """Raised if a command needs to print an error and exit."""
249
249
250 class UnexpectedOutput(Abort):
250 class UnexpectedOutput(Abort):
251 """Raised to print an error with part of output and exit."""
251 """Raised to print an error with part of output and exit."""
252
252
253 def always(fn): return True
253 def always(fn): return True
254 def never(fn): return False
254 def never(fn): return False
255
255
256 def expand_glob(pats):
256 def expand_glob(pats):
257 '''On Windows, expand the implicit globs in a list of patterns'''
257 '''On Windows, expand the implicit globs in a list of patterns'''
258 if os.name != 'nt':
258 if os.name != 'nt':
259 return list(pats)
259 return list(pats)
260 ret = []
260 ret = []
261 for p in pats:
261 for p in pats:
262 kind, name = patkind(p, None)
262 kind, name = patkind(p, None)
263 if kind is None:
263 if kind is None:
264 globbed = glob.glob(name)
264 globbed = glob.glob(name)
265 if globbed:
265 if globbed:
266 ret.extend(globbed)
266 ret.extend(globbed)
267 continue
267 continue
268 # if we couldn't expand the glob, just keep it around
268 # if we couldn't expand the glob, just keep it around
269 ret.append(p)
269 ret.append(p)
270 return ret
270 return ret
271
271
272 def patkind(name, dflt_pat='glob'):
272 def patkind(name, dflt_pat='glob'):
273 """Split a string into an optional pattern kind prefix and the
273 """Split a string into an optional pattern kind prefix and the
274 actual pattern."""
274 actual pattern."""
275 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
275 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
276 if name.startswith(prefix + ':'): return name.split(':', 1)
276 if name.startswith(prefix + ':'): return name.split(':', 1)
277 return dflt_pat, name
277 return dflt_pat, name
278
278
279 def globre(pat, head='^', tail='$'):
279 def globre(pat, head='^', tail='$'):
280 "convert a glob pattern into a regexp"
280 "convert a glob pattern into a regexp"
281 i, n = 0, len(pat)
281 i, n = 0, len(pat)
282 res = ''
282 res = ''
283 group = False
283 group = False
284 def peek(): return i < n and pat[i]
284 def peek(): return i < n and pat[i]
285 while i < n:
285 while i < n:
286 c = pat[i]
286 c = pat[i]
287 i = i+1
287 i = i+1
288 if c == '*':
288 if c == '*':
289 if peek() == '*':
289 if peek() == '*':
290 i += 1
290 i += 1
291 res += '.*'
291 res += '.*'
292 else:
292 else:
293 res += '[^/]*'
293 res += '[^/]*'
294 elif c == '?':
294 elif c == '?':
295 res += '.'
295 res += '.'
296 elif c == '[':
296 elif c == '[':
297 j = i
297 j = i
298 if j < n and pat[j] in '!]':
298 if j < n and pat[j] in '!]':
299 j += 1
299 j += 1
300 while j < n and pat[j] != ']':
300 while j < n and pat[j] != ']':
301 j += 1
301 j += 1
302 if j >= n:
302 if j >= n:
303 res += '\\['
303 res += '\\['
304 else:
304 else:
305 stuff = pat[i:j].replace('\\','\\\\')
305 stuff = pat[i:j].replace('\\','\\\\')
306 i = j + 1
306 i = j + 1
307 if stuff[0] == '!':
307 if stuff[0] == '!':
308 stuff = '^' + stuff[1:]
308 stuff = '^' + stuff[1:]
309 elif stuff[0] == '^':
309 elif stuff[0] == '^':
310 stuff = '\\' + stuff
310 stuff = '\\' + stuff
311 res = '%s[%s]' % (res, stuff)
311 res = '%s[%s]' % (res, stuff)
312 elif c == '{':
312 elif c == '{':
313 group = True
313 group = True
314 res += '(?:'
314 res += '(?:'
315 elif c == '}' and group:
315 elif c == '}' and group:
316 res += ')'
316 res += ')'
317 group = False
317 group = False
318 elif c == ',' and group:
318 elif c == ',' and group:
319 res += '|'
319 res += '|'
320 elif c == '\\':
320 elif c == '\\':
321 p = peek()
321 p = peek()
322 if p:
322 if p:
323 i += 1
323 i += 1
324 res += re.escape(p)
324 res += re.escape(p)
325 else:
325 else:
326 res += re.escape(c)
326 res += re.escape(c)
327 else:
327 else:
328 res += re.escape(c)
328 res += re.escape(c)
329 return head + res + tail
329 return head + res + tail
330
330
331 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
331 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
332
332
333 def pathto(root, n1, n2):
333 def pathto(root, n1, n2):
334 '''return the relative path from one place to another.
334 '''return the relative path from one place to another.
335 root should use os.sep to separate directories
335 root should use os.sep to separate directories
336 n1 should use os.sep to separate directories
336 n1 should use os.sep to separate directories
337 n2 should use "/" to separate directories
337 n2 should use "/" to separate directories
338 returns an os.sep-separated path.
338 returns an os.sep-separated path.
339
339
340 If n1 is a relative path, it's assumed it's
340 If n1 is a relative path, it's assumed it's
341 relative to root.
341 relative to root.
342 n2 should always be relative to root.
342 n2 should always be relative to root.
343 '''
343 '''
344 if not n1: return localpath(n2)
344 if not n1: return localpath(n2)
345 if os.path.isabs(n1):
345 if os.path.isabs(n1):
346 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
346 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
347 return os.path.join(root, localpath(n2))
347 return os.path.join(root, localpath(n2))
348 n2 = '/'.join((pconvert(root), n2))
348 n2 = '/'.join((pconvert(root), n2))
349 a, b = n1.split(os.sep), n2.split('/')
349 a, b = n1.split(os.sep), n2.split('/')
350 a.reverse()
350 a.reverse()
351 b.reverse()
351 b.reverse()
352 while a and b and a[-1] == b[-1]:
352 while a and b and a[-1] == b[-1]:
353 a.pop()
353 a.pop()
354 b.pop()
354 b.pop()
355 b.reverse()
355 b.reverse()
356 return os.sep.join((['..'] * len(a)) + b)
356 return os.sep.join((['..'] * len(a)) + b)
357
357
358 def canonpath(root, cwd, myname):
358 def canonpath(root, cwd, myname):
359 """return the canonical path of myname, given cwd and root"""
359 """return the canonical path of myname, given cwd and root"""
360 if root == os.sep:
360 if root == os.sep:
361 rootsep = os.sep
361 rootsep = os.sep
362 elif root.endswith(os.sep):
362 elif root.endswith(os.sep):
363 rootsep = root
363 rootsep = root
364 else:
364 else:
365 rootsep = root + os.sep
365 rootsep = root + os.sep
366 name = myname
366 name = myname
367 if not os.path.isabs(name):
367 if not os.path.isabs(name):
368 name = os.path.join(root, cwd, name)
368 name = os.path.join(root, cwd, name)
369 name = os.path.normpath(name)
369 name = os.path.normpath(name)
370 audit_path = path_auditor(root)
370 audit_path = path_auditor(root)
371 if name != rootsep and name.startswith(rootsep):
371 if name != rootsep and name.startswith(rootsep):
372 name = name[len(rootsep):]
372 name = name[len(rootsep):]
373 audit_path(name)
373 audit_path(name)
374 return pconvert(name)
374 return pconvert(name)
375 elif name == root:
375 elif name == root:
376 return ''
376 return ''
377 else:
377 else:
378 # Determine whether `name' is in the hierarchy at or beneath `root',
378 # Determine whether `name' is in the hierarchy at or beneath `root',
379 # by iterating name=dirname(name) until that causes no change (can't
379 # by iterating name=dirname(name) until that causes no change (can't
380 # check name == '/', because that doesn't work on windows). For each
380 # check name == '/', because that doesn't work on windows). For each
381 # `name', compare dev/inode numbers. If they match, the list `rel'
381 # `name', compare dev/inode numbers. If they match, the list `rel'
382 # holds the reversed list of components making up the relative file
382 # holds the reversed list of components making up the relative file
383 # name we want.
383 # name we want.
384 root_st = os.stat(root)
384 root_st = os.stat(root)
385 rel = []
385 rel = []
386 while True:
386 while True:
387 try:
387 try:
388 name_st = os.stat(name)
388 name_st = os.stat(name)
389 except OSError:
389 except OSError:
390 break
390 break
391 if samestat(name_st, root_st):
391 if samestat(name_st, root_st):
392 if not rel:
392 if not rel:
393 # name was actually the same as root (maybe a symlink)
393 # name was actually the same as root (maybe a symlink)
394 return ''
394 return ''
395 rel.reverse()
395 rel.reverse()
396 name = os.path.join(*rel)
396 name = os.path.join(*rel)
397 audit_path(name)
397 audit_path(name)
398 return pconvert(name)
398 return pconvert(name)
399 dirname, basename = os.path.split(name)
399 dirname, basename = os.path.split(name)
400 rel.append(basename)
400 rel.append(basename)
401 if dirname == name:
401 if dirname == name:
402 break
402 break
403 name = dirname
403 name = dirname
404
404
405 raise Abort('%s not under root' % myname)
405 raise Abort('%s not under root' % myname)
406
406
407 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
407 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
408 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
408 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
409
409
410 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
410 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
411 globbed=False, default=None):
411 globbed=False, default=None):
412 default = default or 'relpath'
412 default = default or 'relpath'
413 if default == 'relpath' and not globbed:
413 if default == 'relpath' and not globbed:
414 names = expand_glob(names)
414 names = expand_glob(names)
415 return _matcher(canonroot, cwd, names, inc, exc, default, src)
415 return _matcher(canonroot, cwd, names, inc, exc, default, src)
416
416
417 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
417 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
418 """build a function to match a set of file patterns
418 """build a function to match a set of file patterns
419
419
420 arguments:
420 arguments:
421 canonroot - the canonical root of the tree you're matching against
421 canonroot - the canonical root of the tree you're matching against
422 cwd - the current working directory, if relevant
422 cwd - the current working directory, if relevant
423 names - patterns to find
423 names - patterns to find
424 inc - patterns to include
424 inc - patterns to include
425 exc - patterns to exclude
425 exc - patterns to exclude
426 dflt_pat - if a pattern in names has no explicit type, assume this one
426 dflt_pat - if a pattern in names has no explicit type, assume this one
427 src - where these patterns came from (e.g. .hgignore)
427 src - where these patterns came from (e.g. .hgignore)
428
428
429 a pattern is one of:
429 a pattern is one of:
430 'glob:<glob>' - a glob relative to cwd
430 'glob:<glob>' - a glob relative to cwd
431 're:<regexp>' - a regular expression
431 're:<regexp>' - a regular expression
432 'path:<path>' - a path relative to canonroot
432 'path:<path>' - a path relative to canonroot
433 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
433 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
434 'relpath:<path>' - a path relative to cwd
434 'relpath:<path>' - a path relative to cwd
435 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
435 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
436 '<something>' - one of the cases above, selected by the dflt_pat argument
436 '<something>' - one of the cases above, selected by the dflt_pat argument
437
437
438 returns:
438 returns:
439 a 3-tuple containing
439 a 3-tuple containing
440 - list of roots (places where one should start a recursive walk of the fs);
440 - list of roots (places where one should start a recursive walk of the fs);
441 this often matches the explicit non-pattern names passed in, but also
441 this often matches the explicit non-pattern names passed in, but also
442 includes the initial part of glob: patterns that has no glob characters
442 includes the initial part of glob: patterns that has no glob characters
443 - a bool match(filename) function
443 - a bool match(filename) function
444 - a bool indicating if any patterns were passed in
444 - a bool indicating if any patterns were passed in
445 """
445 """
446
446
447 # a common case: no patterns at all
447 # a common case: no patterns at all
448 if not names and not inc and not exc:
448 if not names and not inc and not exc:
449 return [], always, False
449 return [], always, False
450
450
451 def contains_glob(name):
451 def contains_glob(name):
452 for c in name:
452 for c in name:
453 if c in _globchars: return True
453 if c in _globchars: return True
454 return False
454 return False
455
455
456 def regex(kind, name, tail):
456 def regex(kind, name, tail):
457 '''convert a pattern into a regular expression'''
457 '''convert a pattern into a regular expression'''
458 if not name:
458 if not name:
459 return ''
459 return ''
460 if kind == 're':
460 if kind == 're':
461 return name
461 return name
462 elif kind == 'path':
462 elif kind == 'path':
463 return '^' + re.escape(name) + '(?:/|$)'
463 return '^' + re.escape(name) + '(?:/|$)'
464 elif kind == 'relglob':
464 elif kind == 'relglob':
465 return globre(name, '(?:|.*/)', tail)
465 return globre(name, '(?:|.*/)', tail)
466 elif kind == 'relpath':
466 elif kind == 'relpath':
467 return re.escape(name) + '(?:/|$)'
467 return re.escape(name) + '(?:/|$)'
468 elif kind == 'relre':
468 elif kind == 'relre':
469 if name.startswith('^'):
469 if name.startswith('^'):
470 return name
470 return name
471 return '.*' + name
471 return '.*' + name
472 return globre(name, '', tail)
472 return globre(name, '', tail)
473
473
474 def matchfn(pats, tail):
474 def matchfn(pats, tail):
475 """build a matching function from a set of patterns"""
475 """build a matching function from a set of patterns"""
476 if not pats:
476 if not pats:
477 return
477 return
478 try:
478 try:
479 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
479 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
480 return re.compile(pat).match
480 return re.compile(pat).match
481 except OverflowError:
481 except OverflowError:
482 # We're using a Python with a tiny regex engine and we
482 # We're using a Python with a tiny regex engine and we
483 # made it explode, so we'll divide the pattern list in two
483 # made it explode, so we'll divide the pattern list in two
484 # until it works
484 # until it works
485 l = len(pats)
485 l = len(pats)
486 if l < 2:
486 if l < 2:
487 raise
487 raise
488 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
488 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
489 return lambda s: a(s) or b(s)
489 return lambda s: a(s) or b(s)
490 except re.error:
490 except re.error:
491 for k, p in pats:
491 for k, p in pats:
492 try:
492 try:
493 re.compile('(?:%s)' % regex(k, p, tail))
493 re.compile('(?:%s)' % regex(k, p, tail))
494 except re.error:
494 except re.error:
495 if src:
495 if src:
496 raise Abort("%s: invalid pattern (%s): %s" %
496 raise Abort("%s: invalid pattern (%s): %s" %
497 (src, k, p))
497 (src, k, p))
498 else:
498 else:
499 raise Abort("invalid pattern (%s): %s" % (k, p))
499 raise Abort("invalid pattern (%s): %s" % (k, p))
500 raise Abort("invalid pattern")
500 raise Abort("invalid pattern")
501
501
502 def globprefix(pat):
502 def globprefix(pat):
503 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
503 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
504 root = []
504 root = []
505 for p in pat.split('/'):
505 for p in pat.split('/'):
506 if contains_glob(p): break
506 if contains_glob(p): break
507 root.append(p)
507 root.append(p)
508 return '/'.join(root) or '.'
508 return '/'.join(root) or '.'
509
509
510 def normalizepats(names, default):
510 def normalizepats(names, default):
511 pats = []
511 pats = []
512 roots = []
512 roots = []
513 anypats = False
513 anypats = False
514 for kind, name in [patkind(p, default) for p in names]:
514 for kind, name in [patkind(p, default) for p in names]:
515 if kind in ('glob', 'relpath'):
515 if kind in ('glob', 'relpath'):
516 name = canonpath(canonroot, cwd, name)
516 name = canonpath(canonroot, cwd, name)
517 elif kind in ('relglob', 'path'):
517 elif kind in ('relglob', 'path'):
518 name = normpath(name)
518 name = normpath(name)
519
519
520 pats.append((kind, name))
520 pats.append((kind, name))
521
521
522 if kind in ('glob', 're', 'relglob', 'relre'):
522 if kind in ('glob', 're', 'relglob', 'relre'):
523 anypats = True
523 anypats = True
524
524
525 if kind == 'glob':
525 if kind == 'glob':
526 root = globprefix(name)
526 root = globprefix(name)
527 roots.append(root)
527 roots.append(root)
528 elif kind in ('relpath', 'path'):
528 elif kind in ('relpath', 'path'):
529 roots.append(name or '.')
529 roots.append(name or '.')
530 elif kind == 'relglob':
530 elif kind == 'relglob':
531 roots.append('.')
531 roots.append('.')
532 return roots, pats, anypats
532 return roots, pats, anypats
533
533
534 roots, pats, anypats = normalizepats(names, dflt_pat)
534 roots, pats, anypats = normalizepats(names, dflt_pat)
535
535
536 patmatch = matchfn(pats, '$') or always
536 patmatch = matchfn(pats, '$') or always
537 incmatch = always
537 incmatch = always
538 if inc:
538 if inc:
539 dummy, inckinds, dummy = normalizepats(inc, 'glob')
539 dummy, inckinds, dummy = normalizepats(inc, 'glob')
540 incmatch = matchfn(inckinds, '(?:/|$)')
540 incmatch = matchfn(inckinds, '(?:/|$)')
541 excmatch = lambda fn: False
541 excmatch = lambda fn: False
542 if exc:
542 if exc:
543 dummy, exckinds, dummy = normalizepats(exc, 'glob')
543 dummy, exckinds, dummy = normalizepats(exc, 'glob')
544 excmatch = matchfn(exckinds, '(?:/|$)')
544 excmatch = matchfn(exckinds, '(?:/|$)')
545
545
546 if not names and inc and not exc:
546 if not names and inc and not exc:
547 # common case: hgignore patterns
547 # common case: hgignore patterns
548 match = incmatch
548 match = incmatch
549 else:
549 else:
550 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
550 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
551
551
552 return (roots, match, (inc or exc or anypats) and True)
552 return (roots, match, (inc or exc or anypats) and True)
553
553
554 _hgexecutable = None
554 _hgexecutable = None
555
555
556 def hgexecutable():
556 def hgexecutable():
557 """return location of the 'hg' executable.
557 """return location of the 'hg' executable.
558
558
559 Defaults to $HG or 'hg' in the search path.
559 Defaults to $HG or 'hg' in the search path.
560 """
560 """
561 if _hgexecutable is None:
561 if _hgexecutable is None:
562 set_hgexecutable(os.environ.get('HG') or find_exe('hg', 'hg'))
562 set_hgexecutable(os.environ.get('HG') or find_exe('hg', 'hg'))
563 return _hgexecutable
563 return _hgexecutable
564
564
565 def set_hgexecutable(path):
565 def set_hgexecutable(path):
566 """set location of the 'hg' executable"""
566 """set location of the 'hg' executable"""
567 global _hgexecutable
567 global _hgexecutable
568 _hgexecutable = path
568 _hgexecutable = path
569
569
570 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
570 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
571 '''enhanced shell command execution.
571 '''enhanced shell command execution.
572 run with environment maybe modified, maybe in different dir.
572 run with environment maybe modified, maybe in different dir.
573
573
574 if command fails and onerr is None, return status. if ui object,
574 if command fails and onerr is None, return status. if ui object,
575 print error message and return status, else raise onerr object as
575 print error message and return status, else raise onerr object as
576 exception.'''
576 exception.'''
577 def py2shell(val):
577 def py2shell(val):
578 'convert python object into string that is useful to shell'
578 'convert python object into string that is useful to shell'
579 if val in (None, False):
579 if val in (None, False):
580 return '0'
580 return '0'
581 if val == True:
581 if val == True:
582 return '1'
582 return '1'
583 return str(val)
583 return str(val)
584 oldenv = {}
584 oldenv = {}
585 for k in environ:
585 for k in environ:
586 oldenv[k] = os.environ.get(k)
586 oldenv[k] = os.environ.get(k)
587 if cwd is not None:
587 if cwd is not None:
588 oldcwd = os.getcwd()
588 oldcwd = os.getcwd()
589 origcmd = cmd
589 origcmd = cmd
590 if os.name == 'nt':
590 if os.name == 'nt':
591 cmd = '"%s"' % cmd
591 cmd = '"%s"' % cmd
592 try:
592 try:
593 for k, v in environ.iteritems():
593 for k, v in environ.iteritems():
594 os.environ[k] = py2shell(v)
594 os.environ[k] = py2shell(v)
595 os.environ['HG'] = hgexecutable()
595 os.environ['HG'] = hgexecutable()
596 if cwd is not None and oldcwd != cwd:
596 if cwd is not None and oldcwd != cwd:
597 os.chdir(cwd)
597 os.chdir(cwd)
598 rc = os.system(cmd)
598 rc = os.system(cmd)
599 if sys.platform == 'OpenVMS' and rc & 1:
599 if sys.platform == 'OpenVMS' and rc & 1:
600 rc = 0
600 rc = 0
601 if rc and onerr:
601 if rc and onerr:
602 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
602 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
603 explain_exit(rc)[0])
603 explain_exit(rc)[0])
604 if errprefix:
604 if errprefix:
605 errmsg = '%s: %s' % (errprefix, errmsg)
605 errmsg = '%s: %s' % (errprefix, errmsg)
606 try:
606 try:
607 onerr.warn(errmsg + '\n')
607 onerr.warn(errmsg + '\n')
608 except AttributeError:
608 except AttributeError:
609 raise onerr(errmsg)
609 raise onerr(errmsg)
610 return rc
610 return rc
611 finally:
611 finally:
612 for k, v in oldenv.iteritems():
612 for k, v in oldenv.iteritems():
613 if v is None:
613 if v is None:
614 del os.environ[k]
614 del os.environ[k]
615 else:
615 else:
616 os.environ[k] = v
616 os.environ[k] = v
617 if cwd is not None and oldcwd != cwd:
617 if cwd is not None and oldcwd != cwd:
618 os.chdir(oldcwd)
618 os.chdir(oldcwd)
619
619
620 # os.path.lexists is not available on python2.3
620 # os.path.lexists is not available on python2.3
621 def lexists(filename):
621 def lexists(filename):
622 "test whether a file with this name exists. does not follow symlinks"
622 "test whether a file with this name exists. does not follow symlinks"
623 try:
623 try:
624 os.lstat(filename)
624 os.lstat(filename)
625 except:
625 except:
626 return False
626 return False
627 return True
627 return True
628
628
629 def rename(src, dst):
629 def rename(src, dst):
630 """forcibly rename a file"""
630 """forcibly rename a file"""
631 try:
631 try:
632 os.rename(src, dst)
632 os.rename(src, dst)
633 except OSError, err: # FIXME: check err (EEXIST ?)
633 except OSError, err: # FIXME: check err (EEXIST ?)
634 # on windows, rename to existing file is not allowed, so we
634 # on windows, rename to existing file is not allowed, so we
635 # must delete destination first. but if file is open, unlink
635 # must delete destination first. but if file is open, unlink
636 # schedules it for delete but does not delete it. rename
636 # schedules it for delete but does not delete it. rename
637 # happens immediately even for open files, so we create
637 # happens immediately even for open files, so we create
638 # temporary file, delete it, rename destination to that name,
638 # temporary file, delete it, rename destination to that name,
639 # then delete that. then rename is safe to do.
639 # then delete that. then rename is safe to do.
640 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
640 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
641 os.close(fd)
641 os.close(fd)
642 os.unlink(temp)
642 os.unlink(temp)
643 os.rename(dst, temp)
643 os.rename(dst, temp)
644 os.unlink(temp)
644 os.unlink(temp)
645 os.rename(src, dst)
645 os.rename(src, dst)
646
646
647 def unlink(f):
647 def unlink(f):
648 """unlink and remove the directory if it is empty"""
648 """unlink and remove the directory if it is empty"""
649 os.unlink(f)
649 os.unlink(f)
650 # try removing directories that might now be empty
650 # try removing directories that might now be empty
651 try:
651 try:
652 os.removedirs(os.path.dirname(f))
652 os.removedirs(os.path.dirname(f))
653 except OSError:
653 except OSError:
654 pass
654 pass
655
655
656 def copyfile(src, dest):
656 def copyfile(src, dest):
657 "copy a file, preserving mode"
657 "copy a file, preserving mode"
658 if os.path.islink(src):
658 if os.path.islink(src):
659 try:
659 try:
660 os.unlink(dest)
660 os.unlink(dest)
661 except:
661 except:
662 pass
662 pass
663 os.symlink(os.readlink(src), dest)
663 os.symlink(os.readlink(src), dest)
664 else:
664 else:
665 try:
665 try:
666 shutil.copyfile(src, dest)
666 shutil.copyfile(src, dest)
667 shutil.copymode(src, dest)
667 shutil.copymode(src, dest)
668 except shutil.Error, inst:
668 except shutil.Error, inst:
669 raise Abort(str(inst))
669 raise Abort(str(inst))
670
670
671 def copyfiles(src, dst, hardlink=None):
671 def copyfiles(src, dst, hardlink=None):
672 """Copy a directory tree using hardlinks if possible"""
672 """Copy a directory tree using hardlinks if possible"""
673
673
674 if hardlink is None:
674 if hardlink is None:
675 hardlink = (os.stat(src).st_dev ==
675 hardlink = (os.stat(src).st_dev ==
676 os.stat(os.path.dirname(dst)).st_dev)
676 os.stat(os.path.dirname(dst)).st_dev)
677
677
678 if os.path.isdir(src):
678 if os.path.isdir(src):
679 os.mkdir(dst)
679 os.mkdir(dst)
680 for name, kind in osutil.listdir(src):
680 for name, kind in osutil.listdir(src):
681 srcname = os.path.join(src, name)
681 srcname = os.path.join(src, name)
682 dstname = os.path.join(dst, name)
682 dstname = os.path.join(dst, name)
683 copyfiles(srcname, dstname, hardlink)
683 copyfiles(srcname, dstname, hardlink)
684 else:
684 else:
685 if hardlink:
685 if hardlink:
686 try:
686 try:
687 os_link(src, dst)
687 os_link(src, dst)
688 except (IOError, OSError):
688 except (IOError, OSError):
689 hardlink = False
689 hardlink = False
690 shutil.copy(src, dst)
690 shutil.copy(src, dst)
691 else:
691 else:
692 shutil.copy(src, dst)
692 shutil.copy(src, dst)
693
693
694 class path_auditor(object):
694 class path_auditor(object):
695 '''ensure that a filesystem path contains no banned components.
695 '''ensure that a filesystem path contains no banned components.
696 the following properties of a path are checked:
696 the following properties of a path are checked:
697
697
698 - under top-level .hg
698 - under top-level .hg
699 - starts at the root of a windows drive
699 - starts at the root of a windows drive
700 - contains ".."
700 - contains ".."
701 - traverses a symlink (e.g. a/symlink_here/b)
701 - traverses a symlink (e.g. a/symlink_here/b)
702 - inside a nested repository'''
702 - inside a nested repository'''
703
703
704 def __init__(self, root):
704 def __init__(self, root):
705 self.audited = set()
705 self.audited = set()
706 self.auditeddir = set()
706 self.auditeddir = set()
707 self.root = root
707 self.root = root
708
708
709 def __call__(self, path):
709 def __call__(self, path):
710 if path in self.audited:
710 if path in self.audited:
711 return
711 return
712 normpath = os.path.normcase(path)
712 normpath = os.path.normcase(path)
713 parts = normpath.split(os.sep)
713 parts = normpath.split(os.sep)
714 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
714 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
715 or os.pardir in parts):
715 or os.pardir in parts):
716 raise Abort(_("path contains illegal component: %s") % path)
716 raise Abort(_("path contains illegal component: %s") % path)
717 def check(prefix):
717 def check(prefix):
718 curpath = os.path.join(self.root, prefix)
718 curpath = os.path.join(self.root, prefix)
719 try:
719 try:
720 st = os.lstat(curpath)
720 st = os.lstat(curpath)
721 except OSError, err:
721 except OSError, err:
722 # EINVAL can be raised as invalid path syntax under win32.
722 # EINVAL can be raised as invalid path syntax under win32.
723 # They must be ignored for patterns can be checked too.
723 # They must be ignored for patterns can be checked too.
724 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
724 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
725 raise
725 raise
726 else:
726 else:
727 if stat.S_ISLNK(st.st_mode):
727 if stat.S_ISLNK(st.st_mode):
728 raise Abort(_('path %r traverses symbolic link %r') %
728 raise Abort(_('path %r traverses symbolic link %r') %
729 (path, prefix))
729 (path, prefix))
730 elif (stat.S_ISDIR(st.st_mode) and
730 elif (stat.S_ISDIR(st.st_mode) and
731 os.path.isdir(os.path.join(curpath, '.hg'))):
731 os.path.isdir(os.path.join(curpath, '.hg'))):
732 raise Abort(_('path %r is inside repo %r') %
732 raise Abort(_('path %r is inside repo %r') %
733 (path, prefix))
733 (path, prefix))
734
734
735 prefixes = []
735 prefixes = []
736 for c in strutil.rfindall(normpath, os.sep):
736 for c in strutil.rfindall(normpath, os.sep):
737 prefix = normpath[:c]
737 prefix = normpath[:c]
738 if prefix in self.auditeddir:
738 if prefix in self.auditeddir:
739 break
739 break
740 check(prefix)
740 check(prefix)
741 prefixes.append(prefix)
741 prefixes.append(prefix)
742
742
743 self.audited.add(path)
743 self.audited.add(path)
744 # only add prefixes to the cache after checking everything: we don't
744 # only add prefixes to the cache after checking everything: we don't
745 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
745 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
746 self.auditeddir.update(prefixes)
746 self.auditeddir.update(prefixes)
747
747
748 def _makelock_file(info, pathname):
748 def _makelock_file(info, pathname):
749 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
749 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
750 os.write(ld, info)
750 os.write(ld, info)
751 os.close(ld)
751 os.close(ld)
752
752
753 def _readlock_file(pathname):
753 def _readlock_file(pathname):
754 return posixfile(pathname).read()
754 return posixfile(pathname).read()
755
755
756 def nlinks(pathname):
756 def nlinks(pathname):
757 """Return number of hardlinks for the given file."""
757 """Return number of hardlinks for the given file."""
758 return os.lstat(pathname).st_nlink
758 return os.lstat(pathname).st_nlink
759
759
760 if hasattr(os, 'link'):
760 if hasattr(os, 'link'):
761 os_link = os.link
761 os_link = os.link
762 else:
762 else:
763 def os_link(src, dst):
763 def os_link(src, dst):
764 raise OSError(0, _("Hardlinks not supported"))
764 raise OSError(0, _("Hardlinks not supported"))
765
765
766 def fstat(fp):
766 def fstat(fp):
767 '''stat file object that may not have fileno method.'''
767 '''stat file object that may not have fileno method.'''
768 try:
768 try:
769 return os.fstat(fp.fileno())
769 return os.fstat(fp.fileno())
770 except AttributeError:
770 except AttributeError:
771 return os.stat(fp.name)
771 return os.stat(fp.name)
772
772
773 posixfile = file
773 posixfile = file
774
774
775 def is_win_9x():
775 def is_win_9x():
776 '''return true if run on windows 95, 98 or me.'''
776 '''return true if run on windows 95, 98 or me.'''
777 try:
777 try:
778 return sys.getwindowsversion()[3] == 1
778 return sys.getwindowsversion()[3] == 1
779 except AttributeError:
779 except AttributeError:
780 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
780 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
781
781
782 getuser_fallback = None
782 getuser_fallback = None
783
783
784 def getuser():
784 def getuser():
785 '''return name of current user'''
785 '''return name of current user'''
786 try:
786 try:
787 return getpass.getuser()
787 return getpass.getuser()
788 except ImportError:
788 except ImportError:
789 # import of pwd will fail on windows - try fallback
789 # import of pwd will fail on windows - try fallback
790 if getuser_fallback:
790 if getuser_fallback:
791 return getuser_fallback()
791 return getuser_fallback()
792 # raised if win32api not available
792 # raised if win32api not available
793 raise Abort(_('user name not available - set USERNAME '
793 raise Abort(_('user name not available - set USERNAME '
794 'environment variable'))
794 'environment variable'))
795
795
796 def username(uid=None):
796 def username(uid=None):
797 """Return the name of the user with the given uid.
797 """Return the name of the user with the given uid.
798
798
799 If uid is None, return the name of the current user."""
799 If uid is None, return the name of the current user."""
800 try:
800 try:
801 import pwd
801 import pwd
802 if uid is None:
802 if uid is None:
803 uid = os.getuid()
803 uid = os.getuid()
804 try:
804 try:
805 return pwd.getpwuid(uid)[0]
805 return pwd.getpwuid(uid)[0]
806 except KeyError:
806 except KeyError:
807 return str(uid)
807 return str(uid)
808 except ImportError:
808 except ImportError:
809 return None
809 return None
810
810
811 def groupname(gid=None):
811 def groupname(gid=None):
812 """Return the name of the group with the given gid.
812 """Return the name of the group with the given gid.
813
813
814 If gid is None, return the name of the current group."""
814 If gid is None, return the name of the current group."""
815 try:
815 try:
816 import grp
816 import grp
817 if gid is None:
817 if gid is None:
818 gid = os.getgid()
818 gid = os.getgid()
819 try:
819 try:
820 return grp.getgrgid(gid)[0]
820 return grp.getgrgid(gid)[0]
821 except KeyError:
821 except KeyError:
822 return str(gid)
822 return str(gid)
823 except ImportError:
823 except ImportError:
824 return None
824 return None
825
825
826 # File system features
826 # File system features
827
827
828 def checkfolding(path):
828 def checkfolding(path):
829 """
829 """
830 Check whether the given path is on a case-sensitive filesystem
830 Check whether the given path is on a case-sensitive filesystem
831
831
832 Requires a path (like /foo/.hg) ending with a foldable final
832 Requires a path (like /foo/.hg) ending with a foldable final
833 directory component.
833 directory component.
834 """
834 """
835 s1 = os.stat(path)
835 s1 = os.stat(path)
836 d, b = os.path.split(path)
836 d, b = os.path.split(path)
837 p2 = os.path.join(d, b.upper())
837 p2 = os.path.join(d, b.upper())
838 if path == p2:
838 if path == p2:
839 p2 = os.path.join(d, b.lower())
839 p2 = os.path.join(d, b.lower())
840 try:
840 try:
841 s2 = os.stat(p2)
841 s2 = os.stat(p2)
842 if s2 == s1:
842 if s2 == s1:
843 return False
843 return False
844 return True
844 return True
845 except:
845 except:
846 return True
846 return True
847
847
848 def checkexec(path):
848 def checkexec(path):
849 """
849 """
850 Check whether the given path is on a filesystem with UNIX-like exec flags
850 Check whether the given path is on a filesystem with UNIX-like exec flags
851
851
852 Requires a directory (like /foo/.hg)
852 Requires a directory (like /foo/.hg)
853 """
853 """
854 try:
854 try:
855 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
855 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
856 fh, fn = tempfile.mkstemp("", "", path)
856 fh, fn = tempfile.mkstemp("", "", path)
857 os.close(fh)
857 os.close(fh)
858 m = os.stat(fn).st_mode
858 m = os.stat(fn).st_mode
859 # VFAT on Linux can flip mode but it doesn't persist a FS remount.
859 # VFAT on Linux can flip mode but it doesn't persist a FS remount.
860 # frequently we can detect it if files are created with exec bit on.
860 # frequently we can detect it if files are created with exec bit on.
861 new_file_has_exec = m & EXECFLAGS
861 new_file_has_exec = m & EXECFLAGS
862 os.chmod(fn, m ^ EXECFLAGS)
862 os.chmod(fn, m ^ EXECFLAGS)
863 exec_flags_cannot_flip = (os.stat(fn).st_mode == m)
863 exec_flags_cannot_flip = (os.stat(fn).st_mode == m)
864 os.unlink(fn)
864 os.unlink(fn)
865 except (IOError,OSError):
865 except (IOError,OSError):
866 # we don't care, the user probably won't be able to commit anyway
866 # we don't care, the user probably won't be able to commit anyway
867 return False
867 return False
868 return not (new_file_has_exec or exec_flags_cannot_flip)
868 return not (new_file_has_exec or exec_flags_cannot_flip)
869
869
870 def execfunc(path, fallback):
870 def execfunc(path, fallback):
871 '''return an is_exec() function with default to fallback'''
871 '''return an is_exec() function with default to fallback'''
872 if checkexec(path):
872 if checkexec(path):
873 return lambda x: is_exec(os.path.join(path, x))
873 return lambda x: is_exec(os.path.join(path, x))
874 return fallback
874 return fallback
875
875
876 def checklink(path):
876 def checklink(path):
877 """check whether the given path is on a symlink-capable filesystem"""
877 """check whether the given path is on a symlink-capable filesystem"""
878 # mktemp is not racy because symlink creation will fail if the
878 # mktemp is not racy because symlink creation will fail if the
879 # file already exists
879 # file already exists
880 name = tempfile.mktemp(dir=path)
880 name = tempfile.mktemp(dir=path)
881 try:
881 try:
882 os.symlink(".", name)
882 os.symlink(".", name)
883 os.unlink(name)
883 os.unlink(name)
884 return True
884 return True
885 except (OSError, AttributeError):
885 except (OSError, AttributeError):
886 return False
886 return False
887
887
888 def linkfunc(path, fallback):
888 def linkfunc(path, fallback):
889 '''return an is_link() function with default to fallback'''
889 '''return an is_link() function with default to fallback'''
890 if checklink(path):
890 if checklink(path):
891 return lambda x: os.path.islink(os.path.join(path, x))
891 return lambda x: os.path.islink(os.path.join(path, x))
892 return fallback
892 return fallback
893
893
894 _umask = os.umask(0)
894 _umask = os.umask(0)
895 os.umask(_umask)
895 os.umask(_umask)
896
896
897 def needbinarypatch():
897 def needbinarypatch():
898 """return True if patches should be applied in binary mode by default."""
898 """return True if patches should be applied in binary mode by default."""
899 return os.name == 'nt'
899 return os.name == 'nt'
900
900
901 # Platform specific variants
901 # Platform specific variants
902 if os.name == 'nt':
902 if os.name == 'nt':
903 import msvcrt
903 import msvcrt
904 nulldev = 'NUL:'
904 nulldev = 'NUL:'
905
905
906 class winstdout:
906 class winstdout:
907 '''stdout on windows misbehaves if sent through a pipe'''
907 '''stdout on windows misbehaves if sent through a pipe'''
908
908
909 def __init__(self, fp):
909 def __init__(self, fp):
910 self.fp = fp
910 self.fp = fp
911
911
912 def __getattr__(self, key):
912 def __getattr__(self, key):
913 return getattr(self.fp, key)
913 return getattr(self.fp, key)
914
914
915 def close(self):
915 def close(self):
916 try:
916 try:
917 self.fp.close()
917 self.fp.close()
918 except: pass
918 except: pass
919
919
920 def write(self, s):
920 def write(self, s):
921 try:
921 try:
922 return self.fp.write(s)
922 # This is workaround for "Not enough space" error on
923 # writing large size of data to console.
924 limit = 16000
925 l = len(s)
926 start = 0
927 while start < l:
928 end = start + limit
929 self.fp.write(s[start:end])
930 start = end
923 except IOError, inst:
931 except IOError, inst:
924 if inst.errno != 0: raise
932 if inst.errno != 0: raise
925 self.close()
933 self.close()
926 raise IOError(errno.EPIPE, 'Broken pipe')
934 raise IOError(errno.EPIPE, 'Broken pipe')
927
935
928 def flush(self):
936 def flush(self):
929 try:
937 try:
930 return self.fp.flush()
938 return self.fp.flush()
931 except IOError, inst:
939 except IOError, inst:
932 if inst.errno != errno.EINVAL: raise
940 if inst.errno != errno.EINVAL: raise
933 self.close()
941 self.close()
934 raise IOError(errno.EPIPE, 'Broken pipe')
942 raise IOError(errno.EPIPE, 'Broken pipe')
935
943
936 sys.stdout = winstdout(sys.stdout)
944 sys.stdout = winstdout(sys.stdout)
937
945
938 def system_rcpath():
946 def system_rcpath():
939 try:
947 try:
940 return system_rcpath_win32()
948 return system_rcpath_win32()
941 except:
949 except:
942 return [r'c:\mercurial\mercurial.ini']
950 return [r'c:\mercurial\mercurial.ini']
943
951
944 def user_rcpath():
952 def user_rcpath():
945 '''return os-specific hgrc search path to the user dir'''
953 '''return os-specific hgrc search path to the user dir'''
946 try:
954 try:
947 userrc = user_rcpath_win32()
955 userrc = user_rcpath_win32()
948 except:
956 except:
949 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
957 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
950 path = [userrc]
958 path = [userrc]
951 userprofile = os.environ.get('USERPROFILE')
959 userprofile = os.environ.get('USERPROFILE')
952 if userprofile:
960 if userprofile:
953 path.append(os.path.join(userprofile, 'mercurial.ini'))
961 path.append(os.path.join(userprofile, 'mercurial.ini'))
954 return path
962 return path
955
963
956 def parse_patch_output(output_line):
964 def parse_patch_output(output_line):
957 """parses the output produced by patch and returns the file name"""
965 """parses the output produced by patch and returns the file name"""
958 pf = output_line[14:]
966 pf = output_line[14:]
959 if pf[0] == '`':
967 if pf[0] == '`':
960 pf = pf[1:-1] # Remove the quotes
968 pf = pf[1:-1] # Remove the quotes
961 return pf
969 return pf
962
970
963 def sshargs(sshcmd, host, user, port):
971 def sshargs(sshcmd, host, user, port):
964 '''Build argument list for ssh or Plink'''
972 '''Build argument list for ssh or Plink'''
965 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
973 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
966 args = user and ("%s@%s" % (user, host)) or host
974 args = user and ("%s@%s" % (user, host)) or host
967 return port and ("%s %s %s" % (args, pflag, port)) or args
975 return port and ("%s %s %s" % (args, pflag, port)) or args
968
976
969 def testpid(pid):
977 def testpid(pid):
970 '''return False if pid dead, True if running or not known'''
978 '''return False if pid dead, True if running or not known'''
971 return True
979 return True
972
980
973 def set_exec(f, mode):
981 def set_exec(f, mode):
974 pass
982 pass
975
983
976 def set_link(f, mode):
984 def set_link(f, mode):
977 pass
985 pass
978
986
979 def set_binary(fd):
987 def set_binary(fd):
980 msvcrt.setmode(fd.fileno(), os.O_BINARY)
988 msvcrt.setmode(fd.fileno(), os.O_BINARY)
981
989
982 def pconvert(path):
990 def pconvert(path):
983 return path.replace("\\", "/")
991 return path.replace("\\", "/")
984
992
985 def localpath(path):
993 def localpath(path):
986 return path.replace('/', '\\')
994 return path.replace('/', '\\')
987
995
988 def normpath(path):
996 def normpath(path):
989 return pconvert(os.path.normpath(path))
997 return pconvert(os.path.normpath(path))
990
998
991 makelock = _makelock_file
999 makelock = _makelock_file
992 readlock = _readlock_file
1000 readlock = _readlock_file
993
1001
994 def samestat(s1, s2):
1002 def samestat(s1, s2):
995 return False
1003 return False
996
1004
997 # A sequence of backslashes is special iff it precedes a double quote:
1005 # A sequence of backslashes is special iff it precedes a double quote:
998 # - if there's an even number of backslashes, the double quote is not
1006 # - if there's an even number of backslashes, the double quote is not
999 # quoted (i.e. it ends the quoted region)
1007 # quoted (i.e. it ends the quoted region)
1000 # - if there's an odd number of backslashes, the double quote is quoted
1008 # - if there's an odd number of backslashes, the double quote is quoted
1001 # - in both cases, every pair of backslashes is unquoted into a single
1009 # - in both cases, every pair of backslashes is unquoted into a single
1002 # backslash
1010 # backslash
1003 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1011 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1004 # So, to quote a string, we must surround it in double quotes, double
1012 # So, to quote a string, we must surround it in double quotes, double
1005 # the number of backslashes that preceed double quotes and add another
1013 # the number of backslashes that preceed double quotes and add another
1006 # backslash before every double quote (being careful with the double
1014 # backslash before every double quote (being careful with the double
1007 # quote we've appended to the end)
1015 # quote we've appended to the end)
1008 _quotere = None
1016 _quotere = None
1009 def shellquote(s):
1017 def shellquote(s):
1010 global _quotere
1018 global _quotere
1011 if _quotere is None:
1019 if _quotere is None:
1012 _quotere = re.compile(r'(\\*)("|\\$)')
1020 _quotere = re.compile(r'(\\*)("|\\$)')
1013 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1021 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1014
1022
1015 def quotecommand(cmd):
1023 def quotecommand(cmd):
1016 """Build a command string suitable for os.popen* calls."""
1024 """Build a command string suitable for os.popen* calls."""
1017 # The extra quotes are needed because popen* runs the command
1025 # The extra quotes are needed because popen* runs the command
1018 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1026 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1019 return '"' + cmd + '"'
1027 return '"' + cmd + '"'
1020
1028
1021 def popen(command):
1029 def popen(command):
1022 # Work around "popen spawned process may not write to stdout
1030 # Work around "popen spawned process may not write to stdout
1023 # under windows"
1031 # under windows"
1024 # http://bugs.python.org/issue1366
1032 # http://bugs.python.org/issue1366
1025 command += " 2> %s" % nulldev
1033 command += " 2> %s" % nulldev
1026 return os.popen(quotecommand(command))
1034 return os.popen(quotecommand(command))
1027
1035
1028 def explain_exit(code):
1036 def explain_exit(code):
1029 return _("exited with status %d") % code, code
1037 return _("exited with status %d") % code, code
1030
1038
1031 # if you change this stub into a real check, please try to implement the
1039 # if you change this stub into a real check, please try to implement the
1032 # username and groupname functions above, too.
1040 # username and groupname functions above, too.
1033 def isowner(fp, st=None):
1041 def isowner(fp, st=None):
1034 return True
1042 return True
1035
1043
1036 def find_in_path(name, path, default=None):
1044 def find_in_path(name, path, default=None):
1037 '''find name in search path. path can be string (will be split
1045 '''find name in search path. path can be string (will be split
1038 with os.pathsep), or iterable thing that returns strings. if name
1046 with os.pathsep), or iterable thing that returns strings. if name
1039 found, return path to name. else return default. name is looked up
1047 found, return path to name. else return default. name is looked up
1040 using cmd.exe rules, using PATHEXT.'''
1048 using cmd.exe rules, using PATHEXT.'''
1041 if isinstance(path, str):
1049 if isinstance(path, str):
1042 path = path.split(os.pathsep)
1050 path = path.split(os.pathsep)
1043
1051
1044 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1052 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1045 pathext = pathext.lower().split(os.pathsep)
1053 pathext = pathext.lower().split(os.pathsep)
1046 isexec = os.path.splitext(name)[1].lower() in pathext
1054 isexec = os.path.splitext(name)[1].lower() in pathext
1047
1055
1048 for p in path:
1056 for p in path:
1049 p_name = os.path.join(p, name)
1057 p_name = os.path.join(p, name)
1050
1058
1051 if isexec and os.path.exists(p_name):
1059 if isexec and os.path.exists(p_name):
1052 return p_name
1060 return p_name
1053
1061
1054 for ext in pathext:
1062 for ext in pathext:
1055 p_name_ext = p_name + ext
1063 p_name_ext = p_name + ext
1056 if os.path.exists(p_name_ext):
1064 if os.path.exists(p_name_ext):
1057 return p_name_ext
1065 return p_name_ext
1058 return default
1066 return default
1059
1067
1060 def set_signal_handler():
1068 def set_signal_handler():
1061 try:
1069 try:
1062 set_signal_handler_win32()
1070 set_signal_handler_win32()
1063 except NameError:
1071 except NameError:
1064 pass
1072 pass
1065
1073
1066 try:
1074 try:
1067 # override functions with win32 versions if possible
1075 # override functions with win32 versions if possible
1068 from util_win32 import *
1076 from util_win32 import *
1069 if not is_win_9x():
1077 if not is_win_9x():
1070 posixfile = posixfile_nt
1078 posixfile = posixfile_nt
1071 except ImportError:
1079 except ImportError:
1072 pass
1080 pass
1073
1081
1074 else:
1082 else:
1075 nulldev = '/dev/null'
1083 nulldev = '/dev/null'
1076
1084
1077 def rcfiles(path):
1085 def rcfiles(path):
1078 rcs = [os.path.join(path, 'hgrc')]
1086 rcs = [os.path.join(path, 'hgrc')]
1079 rcdir = os.path.join(path, 'hgrc.d')
1087 rcdir = os.path.join(path, 'hgrc.d')
1080 try:
1088 try:
1081 rcs.extend([os.path.join(rcdir, f)
1089 rcs.extend([os.path.join(rcdir, f)
1082 for f, kind in osutil.listdir(rcdir)
1090 for f, kind in osutil.listdir(rcdir)
1083 if f.endswith(".rc")])
1091 if f.endswith(".rc")])
1084 except OSError:
1092 except OSError:
1085 pass
1093 pass
1086 return rcs
1094 return rcs
1087
1095
1088 def system_rcpath():
1096 def system_rcpath():
1089 path = []
1097 path = []
1090 # old mod_python does not set sys.argv
1098 # old mod_python does not set sys.argv
1091 if len(getattr(sys, 'argv', [])) > 0:
1099 if len(getattr(sys, 'argv', [])) > 0:
1092 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1100 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1093 '/../etc/mercurial'))
1101 '/../etc/mercurial'))
1094 path.extend(rcfiles('/etc/mercurial'))
1102 path.extend(rcfiles('/etc/mercurial'))
1095 return path
1103 return path
1096
1104
1097 def user_rcpath():
1105 def user_rcpath():
1098 return [os.path.expanduser('~/.hgrc')]
1106 return [os.path.expanduser('~/.hgrc')]
1099
1107
1100 def parse_patch_output(output_line):
1108 def parse_patch_output(output_line):
1101 """parses the output produced by patch and returns the file name"""
1109 """parses the output produced by patch and returns the file name"""
1102 pf = output_line[14:]
1110 pf = output_line[14:]
1103 if os.sys.platform == 'OpenVMS':
1111 if os.sys.platform == 'OpenVMS':
1104 if pf[0] == '`':
1112 if pf[0] == '`':
1105 pf = pf[1:-1] # Remove the quotes
1113 pf = pf[1:-1] # Remove the quotes
1106 else:
1114 else:
1107 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1115 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1108 pf = pf[1:-1] # Remove the quotes
1116 pf = pf[1:-1] # Remove the quotes
1109 return pf
1117 return pf
1110
1118
1111 def sshargs(sshcmd, host, user, port):
1119 def sshargs(sshcmd, host, user, port):
1112 '''Build argument list for ssh'''
1120 '''Build argument list for ssh'''
1113 args = user and ("%s@%s" % (user, host)) or host
1121 args = user and ("%s@%s" % (user, host)) or host
1114 return port and ("%s -p %s" % (args, port)) or args
1122 return port and ("%s -p %s" % (args, port)) or args
1115
1123
1116 def is_exec(f):
1124 def is_exec(f):
1117 """check whether a file is executable"""
1125 """check whether a file is executable"""
1118 return (os.lstat(f).st_mode & 0100 != 0)
1126 return (os.lstat(f).st_mode & 0100 != 0)
1119
1127
1120 def set_exec(f, mode):
1128 def set_exec(f, mode):
1121 s = os.lstat(f).st_mode
1129 s = os.lstat(f).st_mode
1122 if stat.S_ISLNK(s) or (s & 0100 != 0) == mode:
1130 if stat.S_ISLNK(s) or (s & 0100 != 0) == mode:
1123 return
1131 return
1124 if mode:
1132 if mode:
1125 # Turn on +x for every +r bit when making a file executable
1133 # Turn on +x for every +r bit when making a file executable
1126 # and obey umask.
1134 # and obey umask.
1127 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1135 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1128 else:
1136 else:
1129 os.chmod(f, s & 0666)
1137 os.chmod(f, s & 0666)
1130
1138
1131 def set_link(f, mode):
1139 def set_link(f, mode):
1132 """make a file a symbolic link/regular file
1140 """make a file a symbolic link/regular file
1133
1141
1134 if a file is changed to a link, its contents become the link data
1142 if a file is changed to a link, its contents become the link data
1135 if a link is changed to a file, its link data become its contents
1143 if a link is changed to a file, its link data become its contents
1136 """
1144 """
1137
1145
1138 m = os.path.islink(f)
1146 m = os.path.islink(f)
1139 if m == bool(mode):
1147 if m == bool(mode):
1140 return
1148 return
1141
1149
1142 if mode: # switch file to link
1150 if mode: # switch file to link
1143 data = file(f).read()
1151 data = file(f).read()
1144 os.unlink(f)
1152 os.unlink(f)
1145 os.symlink(data, f)
1153 os.symlink(data, f)
1146 else:
1154 else:
1147 data = os.readlink(f)
1155 data = os.readlink(f)
1148 os.unlink(f)
1156 os.unlink(f)
1149 file(f, "w").write(data)
1157 file(f, "w").write(data)
1150
1158
1151 def set_binary(fd):
1159 def set_binary(fd):
1152 pass
1160 pass
1153
1161
1154 def pconvert(path):
1162 def pconvert(path):
1155 return path
1163 return path
1156
1164
1157 def localpath(path):
1165 def localpath(path):
1158 return path
1166 return path
1159
1167
1160 normpath = os.path.normpath
1168 normpath = os.path.normpath
1161 samestat = os.path.samestat
1169 samestat = os.path.samestat
1162
1170
1163 def makelock(info, pathname):
1171 def makelock(info, pathname):
1164 try:
1172 try:
1165 os.symlink(info, pathname)
1173 os.symlink(info, pathname)
1166 except OSError, why:
1174 except OSError, why:
1167 if why.errno == errno.EEXIST:
1175 if why.errno == errno.EEXIST:
1168 raise
1176 raise
1169 else:
1177 else:
1170 _makelock_file(info, pathname)
1178 _makelock_file(info, pathname)
1171
1179
1172 def readlock(pathname):
1180 def readlock(pathname):
1173 try:
1181 try:
1174 return os.readlink(pathname)
1182 return os.readlink(pathname)
1175 except OSError, why:
1183 except OSError, why:
1176 if why.errno in (errno.EINVAL, errno.ENOSYS):
1184 if why.errno in (errno.EINVAL, errno.ENOSYS):
1177 return _readlock_file(pathname)
1185 return _readlock_file(pathname)
1178 else:
1186 else:
1179 raise
1187 raise
1180
1188
1181 def shellquote(s):
1189 def shellquote(s):
1182 if os.sys.platform == 'OpenVMS':
1190 if os.sys.platform == 'OpenVMS':
1183 return '"%s"' % s
1191 return '"%s"' % s
1184 else:
1192 else:
1185 return "'%s'" % s.replace("'", "'\\''")
1193 return "'%s'" % s.replace("'", "'\\''")
1186
1194
1187 def quotecommand(cmd):
1195 def quotecommand(cmd):
1188 return cmd
1196 return cmd
1189
1197
1190 def popen(command):
1198 def popen(command):
1191 return os.popen(command)
1199 return os.popen(command)
1192
1200
1193 def testpid(pid):
1201 def testpid(pid):
1194 '''return False if pid dead, True if running or not sure'''
1202 '''return False if pid dead, True if running or not sure'''
1195 if os.sys.platform == 'OpenVMS':
1203 if os.sys.platform == 'OpenVMS':
1196 return True
1204 return True
1197 try:
1205 try:
1198 os.kill(pid, 0)
1206 os.kill(pid, 0)
1199 return True
1207 return True
1200 except OSError, inst:
1208 except OSError, inst:
1201 return inst.errno != errno.ESRCH
1209 return inst.errno != errno.ESRCH
1202
1210
1203 def explain_exit(code):
1211 def explain_exit(code):
1204 """return a 2-tuple (desc, code) describing a process's status"""
1212 """return a 2-tuple (desc, code) describing a process's status"""
1205 if os.WIFEXITED(code):
1213 if os.WIFEXITED(code):
1206 val = os.WEXITSTATUS(code)
1214 val = os.WEXITSTATUS(code)
1207 return _("exited with status %d") % val, val
1215 return _("exited with status %d") % val, val
1208 elif os.WIFSIGNALED(code):
1216 elif os.WIFSIGNALED(code):
1209 val = os.WTERMSIG(code)
1217 val = os.WTERMSIG(code)
1210 return _("killed by signal %d") % val, val
1218 return _("killed by signal %d") % val, val
1211 elif os.WIFSTOPPED(code):
1219 elif os.WIFSTOPPED(code):
1212 val = os.WSTOPSIG(code)
1220 val = os.WSTOPSIG(code)
1213 return _("stopped by signal %d") % val, val
1221 return _("stopped by signal %d") % val, val
1214 raise ValueError(_("invalid exit code"))
1222 raise ValueError(_("invalid exit code"))
1215
1223
1216 def isowner(fp, st=None):
1224 def isowner(fp, st=None):
1217 """Return True if the file object f belongs to the current user.
1225 """Return True if the file object f belongs to the current user.
1218
1226
1219 The return value of a util.fstat(f) may be passed as the st argument.
1227 The return value of a util.fstat(f) may be passed as the st argument.
1220 """
1228 """
1221 if st is None:
1229 if st is None:
1222 st = fstat(fp)
1230 st = fstat(fp)
1223 return st.st_uid == os.getuid()
1231 return st.st_uid == os.getuid()
1224
1232
1225 def find_in_path(name, path, default=None):
1233 def find_in_path(name, path, default=None):
1226 '''find name in search path. path can be string (will be split
1234 '''find name in search path. path can be string (will be split
1227 with os.pathsep), or iterable thing that returns strings. if name
1235 with os.pathsep), or iterable thing that returns strings. if name
1228 found, return path to name. else return default.'''
1236 found, return path to name. else return default.'''
1229 if isinstance(path, str):
1237 if isinstance(path, str):
1230 path = path.split(os.pathsep)
1238 path = path.split(os.pathsep)
1231 for p in path:
1239 for p in path:
1232 p_name = os.path.join(p, name)
1240 p_name = os.path.join(p, name)
1233 if os.path.exists(p_name):
1241 if os.path.exists(p_name):
1234 return p_name
1242 return p_name
1235 return default
1243 return default
1236
1244
1237 def set_signal_handler():
1245 def set_signal_handler():
1238 pass
1246 pass
1239
1247
1240 def find_exe(name, default=None):
1248 def find_exe(name, default=None):
1241 '''find path of an executable.
1249 '''find path of an executable.
1242 if name contains a path component, return it as is. otherwise,
1250 if name contains a path component, return it as is. otherwise,
1243 use normal executable search path.'''
1251 use normal executable search path.'''
1244
1252
1245 if os.sep in name or sys.platform == 'OpenVMS':
1253 if os.sep in name or sys.platform == 'OpenVMS':
1246 # don't check the executable bit. if the file isn't
1254 # don't check the executable bit. if the file isn't
1247 # executable, whoever tries to actually run it will give a
1255 # executable, whoever tries to actually run it will give a
1248 # much more useful error message.
1256 # much more useful error message.
1249 return name
1257 return name
1250 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1258 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1251
1259
1252 def _buildencodefun():
1260 def _buildencodefun():
1253 e = '_'
1261 e = '_'
1254 win_reserved = [ord(x) for x in '\\:*?"<>|']
1262 win_reserved = [ord(x) for x in '\\:*?"<>|']
1255 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1263 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1256 for x in (range(32) + range(126, 256) + win_reserved):
1264 for x in (range(32) + range(126, 256) + win_reserved):
1257 cmap[chr(x)] = "~%02x" % x
1265 cmap[chr(x)] = "~%02x" % x
1258 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1266 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1259 cmap[chr(x)] = e + chr(x).lower()
1267 cmap[chr(x)] = e + chr(x).lower()
1260 dmap = {}
1268 dmap = {}
1261 for k, v in cmap.iteritems():
1269 for k, v in cmap.iteritems():
1262 dmap[v] = k
1270 dmap[v] = k
1263 def decode(s):
1271 def decode(s):
1264 i = 0
1272 i = 0
1265 while i < len(s):
1273 while i < len(s):
1266 for l in xrange(1, 4):
1274 for l in xrange(1, 4):
1267 try:
1275 try:
1268 yield dmap[s[i:i+l]]
1276 yield dmap[s[i:i+l]]
1269 i += l
1277 i += l
1270 break
1278 break
1271 except KeyError:
1279 except KeyError:
1272 pass
1280 pass
1273 else:
1281 else:
1274 raise KeyError
1282 raise KeyError
1275 return (lambda s: "".join([cmap[c] for c in s]),
1283 return (lambda s: "".join([cmap[c] for c in s]),
1276 lambda s: "".join(list(decode(s))))
1284 lambda s: "".join(list(decode(s))))
1277
1285
1278 encodefilename, decodefilename = _buildencodefun()
1286 encodefilename, decodefilename = _buildencodefun()
1279
1287
1280 def encodedopener(openerfn, fn):
1288 def encodedopener(openerfn, fn):
1281 def o(path, *args, **kw):
1289 def o(path, *args, **kw):
1282 return openerfn(fn(path), *args, **kw)
1290 return openerfn(fn(path), *args, **kw)
1283 return o
1291 return o
1284
1292
1285 def mktempcopy(name, emptyok=False):
1293 def mktempcopy(name, emptyok=False):
1286 """Create a temporary file with the same contents from name
1294 """Create a temporary file with the same contents from name
1287
1295
1288 The permission bits are copied from the original file.
1296 The permission bits are copied from the original file.
1289
1297
1290 If the temporary file is going to be truncated immediately, you
1298 If the temporary file is going to be truncated immediately, you
1291 can use emptyok=True as an optimization.
1299 can use emptyok=True as an optimization.
1292
1300
1293 Returns the name of the temporary file.
1301 Returns the name of the temporary file.
1294 """
1302 """
1295 d, fn = os.path.split(name)
1303 d, fn = os.path.split(name)
1296 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1304 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1297 os.close(fd)
1305 os.close(fd)
1298 # Temporary files are created with mode 0600, which is usually not
1306 # Temporary files are created with mode 0600, which is usually not
1299 # what we want. If the original file already exists, just copy
1307 # what we want. If the original file already exists, just copy
1300 # its mode. Otherwise, manually obey umask.
1308 # its mode. Otherwise, manually obey umask.
1301 try:
1309 try:
1302 st_mode = os.lstat(name).st_mode
1310 st_mode = os.lstat(name).st_mode
1303 except OSError, inst:
1311 except OSError, inst:
1304 if inst.errno != errno.ENOENT:
1312 if inst.errno != errno.ENOENT:
1305 raise
1313 raise
1306 st_mode = 0666 & ~_umask
1314 st_mode = 0666 & ~_umask
1307 os.chmod(temp, st_mode)
1315 os.chmod(temp, st_mode)
1308 if emptyok:
1316 if emptyok:
1309 return temp
1317 return temp
1310 try:
1318 try:
1311 try:
1319 try:
1312 ifp = posixfile(name, "rb")
1320 ifp = posixfile(name, "rb")
1313 except IOError, inst:
1321 except IOError, inst:
1314 if inst.errno == errno.ENOENT:
1322 if inst.errno == errno.ENOENT:
1315 return temp
1323 return temp
1316 if not getattr(inst, 'filename', None):
1324 if not getattr(inst, 'filename', None):
1317 inst.filename = name
1325 inst.filename = name
1318 raise
1326 raise
1319 ofp = posixfile(temp, "wb")
1327 ofp = posixfile(temp, "wb")
1320 for chunk in filechunkiter(ifp):
1328 for chunk in filechunkiter(ifp):
1321 ofp.write(chunk)
1329 ofp.write(chunk)
1322 ifp.close()
1330 ifp.close()
1323 ofp.close()
1331 ofp.close()
1324 except:
1332 except:
1325 try: os.unlink(temp)
1333 try: os.unlink(temp)
1326 except: pass
1334 except: pass
1327 raise
1335 raise
1328 return temp
1336 return temp
1329
1337
1330 class atomictempfile(posixfile):
1338 class atomictempfile(posixfile):
1331 """file-like object that atomically updates a file
1339 """file-like object that atomically updates a file
1332
1340
1333 All writes will be redirected to a temporary copy of the original
1341 All writes will be redirected to a temporary copy of the original
1334 file. When rename is called, the copy is renamed to the original
1342 file. When rename is called, the copy is renamed to the original
1335 name, making the changes visible.
1343 name, making the changes visible.
1336 """
1344 """
1337 def __init__(self, name, mode):
1345 def __init__(self, name, mode):
1338 self.__name = name
1346 self.__name = name
1339 self.temp = mktempcopy(name, emptyok=('w' in mode))
1347 self.temp = mktempcopy(name, emptyok=('w' in mode))
1340 posixfile.__init__(self, self.temp, mode)
1348 posixfile.__init__(self, self.temp, mode)
1341
1349
1342 def rename(self):
1350 def rename(self):
1343 if not self.closed:
1351 if not self.closed:
1344 posixfile.close(self)
1352 posixfile.close(self)
1345 rename(self.temp, localpath(self.__name))
1353 rename(self.temp, localpath(self.__name))
1346
1354
1347 def __del__(self):
1355 def __del__(self):
1348 if not self.closed:
1356 if not self.closed:
1349 try:
1357 try:
1350 os.unlink(self.temp)
1358 os.unlink(self.temp)
1351 except: pass
1359 except: pass
1352 posixfile.close(self)
1360 posixfile.close(self)
1353
1361
1354 class opener(object):
1362 class opener(object):
1355 """Open files relative to a base directory
1363 """Open files relative to a base directory
1356
1364
1357 This class is used to hide the details of COW semantics and
1365 This class is used to hide the details of COW semantics and
1358 remote file access from higher level code.
1366 remote file access from higher level code.
1359 """
1367 """
1360 def __init__(self, base, audit=True):
1368 def __init__(self, base, audit=True):
1361 self.base = base
1369 self.base = base
1362 if audit:
1370 if audit:
1363 self.audit_path = path_auditor(base)
1371 self.audit_path = path_auditor(base)
1364 else:
1372 else:
1365 self.audit_path = always
1373 self.audit_path = always
1366
1374
1367 def __getattr__(self, name):
1375 def __getattr__(self, name):
1368 if name == '_can_symlink':
1376 if name == '_can_symlink':
1369 self._can_symlink = checklink(self.base)
1377 self._can_symlink = checklink(self.base)
1370 return self._can_symlink
1378 return self._can_symlink
1371 raise AttributeError(name)
1379 raise AttributeError(name)
1372
1380
1373 def __call__(self, path, mode="r", text=False, atomictemp=False):
1381 def __call__(self, path, mode="r", text=False, atomictemp=False):
1374 self.audit_path(path)
1382 self.audit_path(path)
1375 f = os.path.join(self.base, path)
1383 f = os.path.join(self.base, path)
1376
1384
1377 if not text and "b" not in mode:
1385 if not text and "b" not in mode:
1378 mode += "b" # for that other OS
1386 mode += "b" # for that other OS
1379
1387
1380 if mode[0] != "r":
1388 if mode[0] != "r":
1381 try:
1389 try:
1382 nlink = nlinks(f)
1390 nlink = nlinks(f)
1383 except OSError:
1391 except OSError:
1384 nlink = 0
1392 nlink = 0
1385 d = os.path.dirname(f)
1393 d = os.path.dirname(f)
1386 if not os.path.isdir(d):
1394 if not os.path.isdir(d):
1387 os.makedirs(d)
1395 os.makedirs(d)
1388 if atomictemp:
1396 if atomictemp:
1389 return atomictempfile(f, mode)
1397 return atomictempfile(f, mode)
1390 if nlink > 1:
1398 if nlink > 1:
1391 rename(mktempcopy(f), f)
1399 rename(mktempcopy(f), f)
1392 return posixfile(f, mode)
1400 return posixfile(f, mode)
1393
1401
1394 def symlink(self, src, dst):
1402 def symlink(self, src, dst):
1395 self.audit_path(dst)
1403 self.audit_path(dst)
1396 linkname = os.path.join(self.base, dst)
1404 linkname = os.path.join(self.base, dst)
1397 try:
1405 try:
1398 os.unlink(linkname)
1406 os.unlink(linkname)
1399 except OSError:
1407 except OSError:
1400 pass
1408 pass
1401
1409
1402 dirname = os.path.dirname(linkname)
1410 dirname = os.path.dirname(linkname)
1403 if not os.path.exists(dirname):
1411 if not os.path.exists(dirname):
1404 os.makedirs(dirname)
1412 os.makedirs(dirname)
1405
1413
1406 if self._can_symlink:
1414 if self._can_symlink:
1407 try:
1415 try:
1408 os.symlink(src, linkname)
1416 os.symlink(src, linkname)
1409 except OSError, err:
1417 except OSError, err:
1410 raise OSError(err.errno, _('could not symlink to %r: %s') %
1418 raise OSError(err.errno, _('could not symlink to %r: %s') %
1411 (src, err.strerror), linkname)
1419 (src, err.strerror), linkname)
1412 else:
1420 else:
1413 f = self(dst, "w")
1421 f = self(dst, "w")
1414 f.write(src)
1422 f.write(src)
1415 f.close()
1423 f.close()
1416
1424
1417 class chunkbuffer(object):
1425 class chunkbuffer(object):
1418 """Allow arbitrary sized chunks of data to be efficiently read from an
1426 """Allow arbitrary sized chunks of data to be efficiently read from an
1419 iterator over chunks of arbitrary size."""
1427 iterator over chunks of arbitrary size."""
1420
1428
1421 def __init__(self, in_iter):
1429 def __init__(self, in_iter):
1422 """in_iter is the iterator that's iterating over the input chunks.
1430 """in_iter is the iterator that's iterating over the input chunks.
1423 targetsize is how big a buffer to try to maintain."""
1431 targetsize is how big a buffer to try to maintain."""
1424 self.iter = iter(in_iter)
1432 self.iter = iter(in_iter)
1425 self.buf = ''
1433 self.buf = ''
1426 self.targetsize = 2**16
1434 self.targetsize = 2**16
1427
1435
1428 def read(self, l):
1436 def read(self, l):
1429 """Read L bytes of data from the iterator of chunks of data.
1437 """Read L bytes of data from the iterator of chunks of data.
1430 Returns less than L bytes if the iterator runs dry."""
1438 Returns less than L bytes if the iterator runs dry."""
1431 if l > len(self.buf) and self.iter:
1439 if l > len(self.buf) and self.iter:
1432 # Clamp to a multiple of self.targetsize
1440 # Clamp to a multiple of self.targetsize
1433 targetsize = max(l, self.targetsize)
1441 targetsize = max(l, self.targetsize)
1434 collector = cStringIO.StringIO()
1442 collector = cStringIO.StringIO()
1435 collector.write(self.buf)
1443 collector.write(self.buf)
1436 collected = len(self.buf)
1444 collected = len(self.buf)
1437 for chunk in self.iter:
1445 for chunk in self.iter:
1438 collector.write(chunk)
1446 collector.write(chunk)
1439 collected += len(chunk)
1447 collected += len(chunk)
1440 if collected >= targetsize:
1448 if collected >= targetsize:
1441 break
1449 break
1442 if collected < targetsize:
1450 if collected < targetsize:
1443 self.iter = False
1451 self.iter = False
1444 self.buf = collector.getvalue()
1452 self.buf = collector.getvalue()
1445 if len(self.buf) == l:
1453 if len(self.buf) == l:
1446 s, self.buf = str(self.buf), ''
1454 s, self.buf = str(self.buf), ''
1447 else:
1455 else:
1448 s, self.buf = self.buf[:l], buffer(self.buf, l)
1456 s, self.buf = self.buf[:l], buffer(self.buf, l)
1449 return s
1457 return s
1450
1458
1451 def filechunkiter(f, size=65536, limit=None):
1459 def filechunkiter(f, size=65536, limit=None):
1452 """Create a generator that produces the data in the file size
1460 """Create a generator that produces the data in the file size
1453 (default 65536) bytes at a time, up to optional limit (default is
1461 (default 65536) bytes at a time, up to optional limit (default is
1454 to read all data). Chunks may be less than size bytes if the
1462 to read all data). Chunks may be less than size bytes if the
1455 chunk is the last chunk in the file, or the file is a socket or
1463 chunk is the last chunk in the file, or the file is a socket or
1456 some other type of file that sometimes reads less data than is
1464 some other type of file that sometimes reads less data than is
1457 requested."""
1465 requested."""
1458 assert size >= 0
1466 assert size >= 0
1459 assert limit is None or limit >= 0
1467 assert limit is None or limit >= 0
1460 while True:
1468 while True:
1461 if limit is None: nbytes = size
1469 if limit is None: nbytes = size
1462 else: nbytes = min(limit, size)
1470 else: nbytes = min(limit, size)
1463 s = nbytes and f.read(nbytes)
1471 s = nbytes and f.read(nbytes)
1464 if not s: break
1472 if not s: break
1465 if limit: limit -= len(s)
1473 if limit: limit -= len(s)
1466 yield s
1474 yield s
1467
1475
1468 def makedate():
1476 def makedate():
1469 lt = time.localtime()
1477 lt = time.localtime()
1470 if lt[8] == 1 and time.daylight:
1478 if lt[8] == 1 and time.daylight:
1471 tz = time.altzone
1479 tz = time.altzone
1472 else:
1480 else:
1473 tz = time.timezone
1481 tz = time.timezone
1474 return time.mktime(lt), tz
1482 return time.mktime(lt), tz
1475
1483
1476 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True, timezone_format=" %+03d%02d"):
1484 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True, timezone_format=" %+03d%02d"):
1477 """represent a (unixtime, offset) tuple as a localized time.
1485 """represent a (unixtime, offset) tuple as a localized time.
1478 unixtime is seconds since the epoch, and offset is the time zone's
1486 unixtime is seconds since the epoch, and offset is the time zone's
1479 number of seconds away from UTC. if timezone is false, do not
1487 number of seconds away from UTC. if timezone is false, do not
1480 append time zone to string."""
1488 append time zone to string."""
1481 t, tz = date or makedate()
1489 t, tz = date or makedate()
1482 s = time.strftime(format, time.gmtime(float(t) - tz))
1490 s = time.strftime(format, time.gmtime(float(t) - tz))
1483 if timezone:
1491 if timezone:
1484 s += timezone_format % (-tz / 3600, ((-tz % 3600) / 60))
1492 s += timezone_format % (-tz / 3600, ((-tz % 3600) / 60))
1485 return s
1493 return s
1486
1494
1487 def strdate(string, format, defaults=[]):
1495 def strdate(string, format, defaults=[]):
1488 """parse a localized time string and return a (unixtime, offset) tuple.
1496 """parse a localized time string and return a (unixtime, offset) tuple.
1489 if the string cannot be parsed, ValueError is raised."""
1497 if the string cannot be parsed, ValueError is raised."""
1490 def timezone(string):
1498 def timezone(string):
1491 tz = string.split()[-1]
1499 tz = string.split()[-1]
1492 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1500 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1493 tz = int(tz)
1501 tz = int(tz)
1494 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1502 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1495 return offset
1503 return offset
1496 if tz == "GMT" or tz == "UTC":
1504 if tz == "GMT" or tz == "UTC":
1497 return 0
1505 return 0
1498 return None
1506 return None
1499
1507
1500 # NOTE: unixtime = localunixtime + offset
1508 # NOTE: unixtime = localunixtime + offset
1501 offset, date = timezone(string), string
1509 offset, date = timezone(string), string
1502 if offset != None:
1510 if offset != None:
1503 date = " ".join(string.split()[:-1])
1511 date = " ".join(string.split()[:-1])
1504
1512
1505 # add missing elements from defaults
1513 # add missing elements from defaults
1506 for part in defaults:
1514 for part in defaults:
1507 found = [True for p in part if ("%"+p) in format]
1515 found = [True for p in part if ("%"+p) in format]
1508 if not found:
1516 if not found:
1509 date += "@" + defaults[part]
1517 date += "@" + defaults[part]
1510 format += "@%" + part[0]
1518 format += "@%" + part[0]
1511
1519
1512 timetuple = time.strptime(date, format)
1520 timetuple = time.strptime(date, format)
1513 localunixtime = int(calendar.timegm(timetuple))
1521 localunixtime = int(calendar.timegm(timetuple))
1514 if offset is None:
1522 if offset is None:
1515 # local timezone
1523 # local timezone
1516 unixtime = int(time.mktime(timetuple))
1524 unixtime = int(time.mktime(timetuple))
1517 offset = unixtime - localunixtime
1525 offset = unixtime - localunixtime
1518 else:
1526 else:
1519 unixtime = localunixtime + offset
1527 unixtime = localunixtime + offset
1520 return unixtime, offset
1528 return unixtime, offset
1521
1529
1522 def parsedate(string, formats=None, defaults=None):
1530 def parsedate(string, formats=None, defaults=None):
1523 """parse a localized time string and return a (unixtime, offset) tuple.
1531 """parse a localized time string and return a (unixtime, offset) tuple.
1524 The date may be a "unixtime offset" string or in one of the specified
1532 The date may be a "unixtime offset" string or in one of the specified
1525 formats."""
1533 formats."""
1526 if not string:
1534 if not string:
1527 return 0, 0
1535 return 0, 0
1528 if not formats:
1536 if not formats:
1529 formats = defaultdateformats
1537 formats = defaultdateformats
1530 string = string.strip()
1538 string = string.strip()
1531 try:
1539 try:
1532 when, offset = map(int, string.split(' '))
1540 when, offset = map(int, string.split(' '))
1533 except ValueError:
1541 except ValueError:
1534 # fill out defaults
1542 # fill out defaults
1535 if not defaults:
1543 if not defaults:
1536 defaults = {}
1544 defaults = {}
1537 now = makedate()
1545 now = makedate()
1538 for part in "d mb yY HI M S".split():
1546 for part in "d mb yY HI M S".split():
1539 if part not in defaults:
1547 if part not in defaults:
1540 if part[0] in "HMS":
1548 if part[0] in "HMS":
1541 defaults[part] = "00"
1549 defaults[part] = "00"
1542 elif part[0] in "dm":
1550 elif part[0] in "dm":
1543 defaults[part] = "1"
1551 defaults[part] = "1"
1544 else:
1552 else:
1545 defaults[part] = datestr(now, "%" + part[0], False)
1553 defaults[part] = datestr(now, "%" + part[0], False)
1546
1554
1547 for format in formats:
1555 for format in formats:
1548 try:
1556 try:
1549 when, offset = strdate(string, format, defaults)
1557 when, offset = strdate(string, format, defaults)
1550 except ValueError:
1558 except ValueError:
1551 pass
1559 pass
1552 else:
1560 else:
1553 break
1561 break
1554 else:
1562 else:
1555 raise Abort(_('invalid date: %r ') % string)
1563 raise Abort(_('invalid date: %r ') % string)
1556 # validate explicit (probably user-specified) date and
1564 # validate explicit (probably user-specified) date and
1557 # time zone offset. values must fit in signed 32 bits for
1565 # time zone offset. values must fit in signed 32 bits for
1558 # current 32-bit linux runtimes. timezones go from UTC-12
1566 # current 32-bit linux runtimes. timezones go from UTC-12
1559 # to UTC+14
1567 # to UTC+14
1560 if abs(when) > 0x7fffffff:
1568 if abs(when) > 0x7fffffff:
1561 raise Abort(_('date exceeds 32 bits: %d') % when)
1569 raise Abort(_('date exceeds 32 bits: %d') % when)
1562 if offset < -50400 or offset > 43200:
1570 if offset < -50400 or offset > 43200:
1563 raise Abort(_('impossible time zone offset: %d') % offset)
1571 raise Abort(_('impossible time zone offset: %d') % offset)
1564 return when, offset
1572 return when, offset
1565
1573
1566 def matchdate(date):
1574 def matchdate(date):
1567 """Return a function that matches a given date match specifier
1575 """Return a function that matches a given date match specifier
1568
1576
1569 Formats include:
1577 Formats include:
1570
1578
1571 '{date}' match a given date to the accuracy provided
1579 '{date}' match a given date to the accuracy provided
1572
1580
1573 '<{date}' on or before a given date
1581 '<{date}' on or before a given date
1574
1582
1575 '>{date}' on or after a given date
1583 '>{date}' on or after a given date
1576
1584
1577 """
1585 """
1578
1586
1579 def lower(date):
1587 def lower(date):
1580 return parsedate(date, extendeddateformats)[0]
1588 return parsedate(date, extendeddateformats)[0]
1581
1589
1582 def upper(date):
1590 def upper(date):
1583 d = dict(mb="12", HI="23", M="59", S="59")
1591 d = dict(mb="12", HI="23", M="59", S="59")
1584 for days in "31 30 29".split():
1592 for days in "31 30 29".split():
1585 try:
1593 try:
1586 d["d"] = days
1594 d["d"] = days
1587 return parsedate(date, extendeddateformats, d)[0]
1595 return parsedate(date, extendeddateformats, d)[0]
1588 except:
1596 except:
1589 pass
1597 pass
1590 d["d"] = "28"
1598 d["d"] = "28"
1591 return parsedate(date, extendeddateformats, d)[0]
1599 return parsedate(date, extendeddateformats, d)[0]
1592
1600
1593 if date[0] == "<":
1601 if date[0] == "<":
1594 when = upper(date[1:])
1602 when = upper(date[1:])
1595 return lambda x: x <= when
1603 return lambda x: x <= when
1596 elif date[0] == ">":
1604 elif date[0] == ">":
1597 when = lower(date[1:])
1605 when = lower(date[1:])
1598 return lambda x: x >= when
1606 return lambda x: x >= when
1599 elif date[0] == "-":
1607 elif date[0] == "-":
1600 try:
1608 try:
1601 days = int(date[1:])
1609 days = int(date[1:])
1602 except ValueError:
1610 except ValueError:
1603 raise Abort(_("invalid day spec: %s") % date[1:])
1611 raise Abort(_("invalid day spec: %s") % date[1:])
1604 when = makedate()[0] - days * 3600 * 24
1612 when = makedate()[0] - days * 3600 * 24
1605 return lambda x: x >= when
1613 return lambda x: x >= when
1606 elif " to " in date:
1614 elif " to " in date:
1607 a, b = date.split(" to ")
1615 a, b = date.split(" to ")
1608 start, stop = lower(a), upper(b)
1616 start, stop = lower(a), upper(b)
1609 return lambda x: x >= start and x <= stop
1617 return lambda x: x >= start and x <= stop
1610 else:
1618 else:
1611 start, stop = lower(date), upper(date)
1619 start, stop = lower(date), upper(date)
1612 return lambda x: x >= start and x <= stop
1620 return lambda x: x >= start and x <= stop
1613
1621
1614 def shortuser(user):
1622 def shortuser(user):
1615 """Return a short representation of a user name or email address."""
1623 """Return a short representation of a user name or email address."""
1616 f = user.find('@')
1624 f = user.find('@')
1617 if f >= 0:
1625 if f >= 0:
1618 user = user[:f]
1626 user = user[:f]
1619 f = user.find('<')
1627 f = user.find('<')
1620 if f >= 0:
1628 if f >= 0:
1621 user = user[f+1:]
1629 user = user[f+1:]
1622 f = user.find(' ')
1630 f = user.find(' ')
1623 if f >= 0:
1631 if f >= 0:
1624 user = user[:f]
1632 user = user[:f]
1625 f = user.find('.')
1633 f = user.find('.')
1626 if f >= 0:
1634 if f >= 0:
1627 user = user[:f]
1635 user = user[:f]
1628 return user
1636 return user
1629
1637
1630 def ellipsis(text, maxlength=400):
1638 def ellipsis(text, maxlength=400):
1631 """Trim string to at most maxlength (default: 400) characters."""
1639 """Trim string to at most maxlength (default: 400) characters."""
1632 if len(text) <= maxlength:
1640 if len(text) <= maxlength:
1633 return text
1641 return text
1634 else:
1642 else:
1635 return "%s..." % (text[:maxlength-3])
1643 return "%s..." % (text[:maxlength-3])
1636
1644
1637 def walkrepos(path):
1645 def walkrepos(path):
1638 '''yield every hg repository under path, recursively.'''
1646 '''yield every hg repository under path, recursively.'''
1639 def errhandler(err):
1647 def errhandler(err):
1640 if err.filename == path:
1648 if err.filename == path:
1641 raise err
1649 raise err
1642
1650
1643 for root, dirs, files in os.walk(path, onerror=errhandler):
1651 for root, dirs, files in os.walk(path, onerror=errhandler):
1644 for d in dirs:
1652 for d in dirs:
1645 if d == '.hg':
1653 if d == '.hg':
1646 yield root
1654 yield root
1647 dirs[:] = []
1655 dirs[:] = []
1648 break
1656 break
1649
1657
1650 _rcpath = None
1658 _rcpath = None
1651
1659
1652 def os_rcpath():
1660 def os_rcpath():
1653 '''return default os-specific hgrc search path'''
1661 '''return default os-specific hgrc search path'''
1654 path = system_rcpath()
1662 path = system_rcpath()
1655 path.extend(user_rcpath())
1663 path.extend(user_rcpath())
1656 path = [os.path.normpath(f) for f in path]
1664 path = [os.path.normpath(f) for f in path]
1657 return path
1665 return path
1658
1666
1659 def rcpath():
1667 def rcpath():
1660 '''return hgrc search path. if env var HGRCPATH is set, use it.
1668 '''return hgrc search path. if env var HGRCPATH is set, use it.
1661 for each item in path, if directory, use files ending in .rc,
1669 for each item in path, if directory, use files ending in .rc,
1662 else use item.
1670 else use item.
1663 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1671 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1664 if no HGRCPATH, use default os-specific path.'''
1672 if no HGRCPATH, use default os-specific path.'''
1665 global _rcpath
1673 global _rcpath
1666 if _rcpath is None:
1674 if _rcpath is None:
1667 if 'HGRCPATH' in os.environ:
1675 if 'HGRCPATH' in os.environ:
1668 _rcpath = []
1676 _rcpath = []
1669 for p in os.environ['HGRCPATH'].split(os.pathsep):
1677 for p in os.environ['HGRCPATH'].split(os.pathsep):
1670 if not p: continue
1678 if not p: continue
1671 if os.path.isdir(p):
1679 if os.path.isdir(p):
1672 for f, kind in osutil.listdir(p):
1680 for f, kind in osutil.listdir(p):
1673 if f.endswith('.rc'):
1681 if f.endswith('.rc'):
1674 _rcpath.append(os.path.join(p, f))
1682 _rcpath.append(os.path.join(p, f))
1675 else:
1683 else:
1676 _rcpath.append(p)
1684 _rcpath.append(p)
1677 else:
1685 else:
1678 _rcpath = os_rcpath()
1686 _rcpath = os_rcpath()
1679 return _rcpath
1687 return _rcpath
1680
1688
1681 def bytecount(nbytes):
1689 def bytecount(nbytes):
1682 '''return byte count formatted as readable string, with units'''
1690 '''return byte count formatted as readable string, with units'''
1683
1691
1684 units = (
1692 units = (
1685 (100, 1<<30, _('%.0f GB')),
1693 (100, 1<<30, _('%.0f GB')),
1686 (10, 1<<30, _('%.1f GB')),
1694 (10, 1<<30, _('%.1f GB')),
1687 (1, 1<<30, _('%.2f GB')),
1695 (1, 1<<30, _('%.2f GB')),
1688 (100, 1<<20, _('%.0f MB')),
1696 (100, 1<<20, _('%.0f MB')),
1689 (10, 1<<20, _('%.1f MB')),
1697 (10, 1<<20, _('%.1f MB')),
1690 (1, 1<<20, _('%.2f MB')),
1698 (1, 1<<20, _('%.2f MB')),
1691 (100, 1<<10, _('%.0f KB')),
1699 (100, 1<<10, _('%.0f KB')),
1692 (10, 1<<10, _('%.1f KB')),
1700 (10, 1<<10, _('%.1f KB')),
1693 (1, 1<<10, _('%.2f KB')),
1701 (1, 1<<10, _('%.2f KB')),
1694 (1, 1, _('%.0f bytes')),
1702 (1, 1, _('%.0f bytes')),
1695 )
1703 )
1696
1704
1697 for multiplier, divisor, format in units:
1705 for multiplier, divisor, format in units:
1698 if nbytes >= divisor * multiplier:
1706 if nbytes >= divisor * multiplier:
1699 return format % (nbytes / float(divisor))
1707 return format % (nbytes / float(divisor))
1700 return units[-1][2] % nbytes
1708 return units[-1][2] % nbytes
1701
1709
1702 def drop_scheme(scheme, path):
1710 def drop_scheme(scheme, path):
1703 sc = scheme + ':'
1711 sc = scheme + ':'
1704 if path.startswith(sc):
1712 if path.startswith(sc):
1705 path = path[len(sc):]
1713 path = path[len(sc):]
1706 if path.startswith('//'):
1714 if path.startswith('//'):
1707 path = path[2:]
1715 path = path[2:]
1708 return path
1716 return path
1709
1717
1710 def uirepr(s):
1718 def uirepr(s):
1711 # Avoid double backslash in Windows path repr()
1719 # Avoid double backslash in Windows path repr()
1712 return repr(s).replace('\\\\', '\\')
1720 return repr(s).replace('\\\\', '\\')
1713
1721
1714 def hidepassword(url):
1722 def hidepassword(url):
1715 '''replaces the password in the url string by three asterisks (***)
1723 '''replaces the password in the url string by three asterisks (***)
1716
1724
1717 >>> hidepassword('http://www.example.com/some/path#fragment')
1725 >>> hidepassword('http://www.example.com/some/path#fragment')
1718 'http://www.example.com/some/path#fragment'
1726 'http://www.example.com/some/path#fragment'
1719 >>> hidepassword('http://me@www.example.com/some/path#fragment')
1727 >>> hidepassword('http://me@www.example.com/some/path#fragment')
1720 'http://me@www.example.com/some/path#fragment'
1728 'http://me@www.example.com/some/path#fragment'
1721 >>> hidepassword('http://me:simplepw@www.example.com/path#frag')
1729 >>> hidepassword('http://me:simplepw@www.example.com/path#frag')
1722 'http://me:***@www.example.com/path#frag'
1730 'http://me:***@www.example.com/path#frag'
1723 >>> hidepassword('http://me:complex:pw@www.example.com/path#frag')
1731 >>> hidepassword('http://me:complex:pw@www.example.com/path#frag')
1724 'http://me:***@www.example.com/path#frag'
1732 'http://me:***@www.example.com/path#frag'
1725 >>> hidepassword('/path/to/repo')
1733 >>> hidepassword('/path/to/repo')
1726 '/path/to/repo'
1734 '/path/to/repo'
1727 >>> hidepassword('relative/path/to/repo')
1735 >>> hidepassword('relative/path/to/repo')
1728 'relative/path/to/repo'
1736 'relative/path/to/repo'
1729 >>> hidepassword('c:\\\\path\\\\to\\\\repo')
1737 >>> hidepassword('c:\\\\path\\\\to\\\\repo')
1730 'c:\\\\path\\\\to\\\\repo'
1738 'c:\\\\path\\\\to\\\\repo'
1731 >>> hidepassword('c:/path/to/repo')
1739 >>> hidepassword('c:/path/to/repo')
1732 'c:/path/to/repo'
1740 'c:/path/to/repo'
1733 >>> hidepassword('bundle://path/to/bundle')
1741 >>> hidepassword('bundle://path/to/bundle')
1734 'bundle://path/to/bundle'
1742 'bundle://path/to/bundle'
1735 '''
1743 '''
1736 url_parts = list(urlparse.urlparse(url))
1744 url_parts = list(urlparse.urlparse(url))
1737 host_with_pw_pattern = re.compile('^([^:]*):([^@]*)@(.*)$')
1745 host_with_pw_pattern = re.compile('^([^:]*):([^@]*)@(.*)$')
1738 if host_with_pw_pattern.match(url_parts[1]):
1746 if host_with_pw_pattern.match(url_parts[1]):
1739 url_parts[1] = re.sub(host_with_pw_pattern, r'\1:***@\3',
1747 url_parts[1] = re.sub(host_with_pw_pattern, r'\1:***@\3',
1740 url_parts[1])
1748 url_parts[1])
1741 return urlparse.urlunparse(url_parts)
1749 return urlparse.urlunparse(url_parts)
1742
1750
General Comments 0
You need to be logged in to leave comments. Login now