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