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