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