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