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