##// END OF EJS Templates
dispatch: support for $ escaping in shell-alias definition...
Roman Sokolov -
r13392:777cef34 default
parent child Browse files
Show More
@@ -1,648 +1,650 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 def run():
14 def run():
15 "run the command in sys.argv"
15 "run the command in sys.argv"
16 sys.exit(dispatch(sys.argv[1:]))
16 sys.exit(dispatch(sys.argv[1:]))
17
17
18 def dispatch(args):
18 def dispatch(args):
19 "run the command specified in args"
19 "run the command specified in args"
20 try:
20 try:
21 u = uimod.ui()
21 u = uimod.ui()
22 if '--traceback' in args:
22 if '--traceback' in args:
23 u.setconfig('ui', 'traceback', 'on')
23 u.setconfig('ui', 'traceback', 'on')
24 except util.Abort, inst:
24 except util.Abort, inst:
25 sys.stderr.write(_("abort: %s\n") % inst)
25 sys.stderr.write(_("abort: %s\n") % inst)
26 if inst.hint:
26 if inst.hint:
27 sys.stderr.write(_("(%s)\n") % inst.hint)
27 sys.stderr.write(_("(%s)\n") % inst.hint)
28 return -1
28 return -1
29 except error.ParseError, inst:
29 except error.ParseError, inst:
30 if len(inst.args) > 1:
30 if len(inst.args) > 1:
31 sys.stderr.write(_("hg: parse error at %s: %s\n") %
31 sys.stderr.write(_("hg: parse error at %s: %s\n") %
32 (inst.args[1], inst.args[0]))
32 (inst.args[1], inst.args[0]))
33 else:
33 else:
34 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
34 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
35 return -1
35 return -1
36 return _runcatch(u, args)
36 return _runcatch(u, args)
37
37
38 def _runcatch(ui, args):
38 def _runcatch(ui, args):
39 def catchterm(*args):
39 def catchterm(*args):
40 raise error.SignalInterrupt
40 raise error.SignalInterrupt
41
41
42 try:
42 try:
43 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
43 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
44 num = getattr(signal, name, None)
44 num = getattr(signal, name, None)
45 if num:
45 if num:
46 signal.signal(num, catchterm)
46 signal.signal(num, catchterm)
47 except ValueError:
47 except ValueError:
48 pass # happens if called in a thread
48 pass # happens if called in a thread
49
49
50 try:
50 try:
51 try:
51 try:
52 # enter the debugger before command execution
52 # enter the debugger before command execution
53 if '--debugger' in args:
53 if '--debugger' in args:
54 ui.warn(_("entering debugger - "
54 ui.warn(_("entering debugger - "
55 "type c to continue starting hg or h for help\n"))
55 "type c to continue starting hg or h for help\n"))
56 pdb.set_trace()
56 pdb.set_trace()
57 try:
57 try:
58 return _dispatch(ui, args)
58 return _dispatch(ui, args)
59 finally:
59 finally:
60 ui.flush()
60 ui.flush()
61 except:
61 except:
62 # enter the debugger when we hit an exception
62 # enter the debugger when we hit an exception
63 if '--debugger' in args:
63 if '--debugger' in args:
64 traceback.print_exc()
64 traceback.print_exc()
65 pdb.post_mortem(sys.exc_info()[2])
65 pdb.post_mortem(sys.exc_info()[2])
66 ui.traceback()
66 ui.traceback()
67 raise
67 raise
68
68
69 # Global exception handling, alphabetically
69 # Global exception handling, alphabetically
70 # Mercurial-specific first, followed by built-in and library exceptions
70 # Mercurial-specific first, followed by built-in and library exceptions
71 except error.AmbiguousCommand, inst:
71 except error.AmbiguousCommand, inst:
72 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
72 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
73 (inst.args[0], " ".join(inst.args[1])))
73 (inst.args[0], " ".join(inst.args[1])))
74 except error.ParseError, inst:
74 except error.ParseError, inst:
75 if len(inst.args) > 1:
75 if len(inst.args) > 1:
76 ui.warn(_("hg: parse error at %s: %s\n") %
76 ui.warn(_("hg: parse error at %s: %s\n") %
77 (inst.args[1], inst.args[0]))
77 (inst.args[1], inst.args[0]))
78 else:
78 else:
79 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
79 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
80 return -1
80 return -1
81 except error.LockHeld, inst:
81 except error.LockHeld, inst:
82 if inst.errno == errno.ETIMEDOUT:
82 if inst.errno == errno.ETIMEDOUT:
83 reason = _('timed out waiting for lock held by %s') % inst.locker
83 reason = _('timed out waiting for lock held by %s') % inst.locker
84 else:
84 else:
85 reason = _('lock held by %s') % inst.locker
85 reason = _('lock held by %s') % inst.locker
86 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
86 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
87 except error.LockUnavailable, inst:
87 except error.LockUnavailable, inst:
88 ui.warn(_("abort: could not lock %s: %s\n") %
88 ui.warn(_("abort: could not lock %s: %s\n") %
89 (inst.desc or inst.filename, inst.strerror))
89 (inst.desc or inst.filename, inst.strerror))
90 except error.CommandError, inst:
90 except error.CommandError, inst:
91 if inst.args[0]:
91 if inst.args[0]:
92 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
92 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
93 commands.help_(ui, inst.args[0])
93 commands.help_(ui, inst.args[0])
94 else:
94 else:
95 ui.warn(_("hg: %s\n") % inst.args[1])
95 ui.warn(_("hg: %s\n") % inst.args[1])
96 commands.help_(ui, 'shortlist')
96 commands.help_(ui, 'shortlist')
97 except error.RepoError, inst:
97 except error.RepoError, inst:
98 ui.warn(_("abort: %s!\n") % inst)
98 ui.warn(_("abort: %s!\n") % inst)
99 except error.ResponseError, inst:
99 except error.ResponseError, inst:
100 ui.warn(_("abort: %s") % inst.args[0])
100 ui.warn(_("abort: %s") % inst.args[0])
101 if not isinstance(inst.args[1], basestring):
101 if not isinstance(inst.args[1], basestring):
102 ui.warn(" %r\n" % (inst.args[1],))
102 ui.warn(" %r\n" % (inst.args[1],))
103 elif not inst.args[1]:
103 elif not inst.args[1]:
104 ui.warn(_(" empty string\n"))
104 ui.warn(_(" empty string\n"))
105 else:
105 else:
106 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
106 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
107 except error.RevlogError, inst:
107 except error.RevlogError, inst:
108 ui.warn(_("abort: %s!\n") % inst)
108 ui.warn(_("abort: %s!\n") % inst)
109 except error.SignalInterrupt:
109 except error.SignalInterrupt:
110 ui.warn(_("killed!\n"))
110 ui.warn(_("killed!\n"))
111 except error.UnknownCommand, inst:
111 except error.UnknownCommand, inst:
112 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
112 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
113 try:
113 try:
114 # check if the command is in a disabled extension
114 # check if the command is in a disabled extension
115 # (but don't check for extensions themselves)
115 # (but don't check for extensions themselves)
116 commands.help_(ui, inst.args[0], unknowncmd=True)
116 commands.help_(ui, inst.args[0], unknowncmd=True)
117 except error.UnknownCommand:
117 except error.UnknownCommand:
118 commands.help_(ui, 'shortlist')
118 commands.help_(ui, 'shortlist')
119 except util.Abort, inst:
119 except util.Abort, inst:
120 ui.warn(_("abort: %s\n") % inst)
120 ui.warn(_("abort: %s\n") % inst)
121 if inst.hint:
121 if inst.hint:
122 ui.warn(_("(%s)\n") % inst.hint)
122 ui.warn(_("(%s)\n") % inst.hint)
123 except ImportError, inst:
123 except ImportError, inst:
124 ui.warn(_("abort: %s!\n") % inst)
124 ui.warn(_("abort: %s!\n") % inst)
125 m = str(inst).split()[-1]
125 m = str(inst).split()[-1]
126 if m in "mpatch bdiff".split():
126 if m in "mpatch bdiff".split():
127 ui.warn(_("(did you forget to compile extensions?)\n"))
127 ui.warn(_("(did you forget to compile extensions?)\n"))
128 elif m in "zlib".split():
128 elif m in "zlib".split():
129 ui.warn(_("(is your Python install correct?)\n"))
129 ui.warn(_("(is your Python install correct?)\n"))
130 except IOError, inst:
130 except IOError, inst:
131 if hasattr(inst, "code"):
131 if hasattr(inst, "code"):
132 ui.warn(_("abort: %s\n") % inst)
132 ui.warn(_("abort: %s\n") % inst)
133 elif hasattr(inst, "reason"):
133 elif hasattr(inst, "reason"):
134 try: # usually it is in the form (errno, strerror)
134 try: # usually it is in the form (errno, strerror)
135 reason = inst.reason.args[1]
135 reason = inst.reason.args[1]
136 except: # it might be anything, for example a string
136 except: # it might be anything, for example a string
137 reason = inst.reason
137 reason = inst.reason
138 ui.warn(_("abort: error: %s\n") % reason)
138 ui.warn(_("abort: error: %s\n") % reason)
139 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
139 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
140 if ui.debugflag:
140 if ui.debugflag:
141 ui.warn(_("broken pipe\n"))
141 ui.warn(_("broken pipe\n"))
142 elif getattr(inst, "strerror", None):
142 elif getattr(inst, "strerror", None):
143 if getattr(inst, "filename", None):
143 if getattr(inst, "filename", None):
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
145 else:
145 else:
146 ui.warn(_("abort: %s\n") % inst.strerror)
146 ui.warn(_("abort: %s\n") % inst.strerror)
147 else:
147 else:
148 raise
148 raise
149 except OSError, inst:
149 except OSError, inst:
150 if getattr(inst, "filename", None):
150 if getattr(inst, "filename", None):
151 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
151 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
152 else:
152 else:
153 ui.warn(_("abort: %s\n") % inst.strerror)
153 ui.warn(_("abort: %s\n") % inst.strerror)
154 except KeyboardInterrupt:
154 except KeyboardInterrupt:
155 try:
155 try:
156 ui.warn(_("interrupted!\n"))
156 ui.warn(_("interrupted!\n"))
157 except IOError, inst:
157 except IOError, inst:
158 if inst.errno == errno.EPIPE:
158 if inst.errno == errno.EPIPE:
159 if ui.debugflag:
159 if ui.debugflag:
160 ui.warn(_("\nbroken pipe\n"))
160 ui.warn(_("\nbroken pipe\n"))
161 else:
161 else:
162 raise
162 raise
163 except MemoryError:
163 except MemoryError:
164 ui.warn(_("abort: out of memory\n"))
164 ui.warn(_("abort: out of memory\n"))
165 except SystemExit, inst:
165 except SystemExit, inst:
166 # Commands shouldn't sys.exit directly, but give a return code.
166 # Commands shouldn't sys.exit directly, but give a return code.
167 # Just in case catch this and and pass exit code to caller.
167 # Just in case catch this and and pass exit code to caller.
168 return inst.code
168 return inst.code
169 except socket.error, inst:
169 except socket.error, inst:
170 ui.warn(_("abort: %s\n") % inst.args[-1])
170 ui.warn(_("abort: %s\n") % inst.args[-1])
171 except:
171 except:
172 ui.warn(_("** unknown exception encountered,"
172 ui.warn(_("** unknown exception encountered,"
173 " please report by visiting\n"))
173 " please report by visiting\n"))
174 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
174 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
175 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
175 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
176 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
176 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
177 % util.version())
177 % util.version())
178 ui.warn(_("** Extensions loaded: %s\n")
178 ui.warn(_("** Extensions loaded: %s\n")
179 % ", ".join([x[0] for x in extensions.extensions()]))
179 % ", ".join([x[0] for x in extensions.extensions()]))
180 raise
180 raise
181
181
182 return -1
182 return -1
183
183
184 def aliasargs(fn):
184 def aliasargs(fn):
185 if hasattr(fn, 'args'):
185 if hasattr(fn, 'args'):
186 return fn.args
186 return fn.args
187 return []
187 return []
188
188
189 class cmdalias(object):
189 class cmdalias(object):
190 def __init__(self, name, definition, cmdtable):
190 def __init__(self, name, definition, cmdtable):
191 self.name = self.cmd = name
191 self.name = self.cmd = name
192 self.cmdname = ''
192 self.cmdname = ''
193 self.definition = definition
193 self.definition = definition
194 self.args = []
194 self.args = []
195 self.opts = []
195 self.opts = []
196 self.help = ''
196 self.help = ''
197 self.norepo = True
197 self.norepo = True
198 self.badalias = False
198 self.badalias = False
199
199
200 try:
200 try:
201 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
201 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
202 for alias, e in cmdtable.iteritems():
202 for alias, e in cmdtable.iteritems():
203 if e is entry:
203 if e is entry:
204 self.cmd = alias
204 self.cmd = alias
205 break
205 break
206 self.shadows = True
206 self.shadows = True
207 except error.UnknownCommand:
207 except error.UnknownCommand:
208 self.shadows = False
208 self.shadows = False
209
209
210 if not self.definition:
210 if not self.definition:
211 def fn(ui, *args):
211 def fn(ui, *args):
212 ui.warn(_("no definition for alias '%s'\n") % self.name)
212 ui.warn(_("no definition for alias '%s'\n") % self.name)
213 return 1
213 return 1
214 self.fn = fn
214 self.fn = fn
215 self.badalias = True
215 self.badalias = True
216
216
217 return
217 return
218
218
219 if self.definition.startswith('!'):
219 if self.definition.startswith('!'):
220 self.shell = True
220 self.shell = True
221 def fn(ui, *args):
221 def fn(ui, *args):
222 env = {'HG_ARGS': ' '.join((self.name,) + args)}
222 env = {'HG_ARGS': ' '.join((self.name,) + args)}
223 def _checkvar(m):
223 def _checkvar(m):
224 if int(m.groups()[0]) <= len(args):
224 if m.groups()[0] == '$':
225 return m.group()
226 elif int(m.groups()[0]) <= len(args):
225 return m.group()
227 return m.group()
226 else:
228 else:
227 return ''
229 return ''
228 cmd = re.sub(r'\$(\d+)', _checkvar, self.definition[1:])
230 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
229 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
231 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
230 replace['0'] = self.name
232 replace['0'] = self.name
231 replace['@'] = ' '.join(args)
233 replace['@'] = ' '.join(args)
232 cmd = util.interpolate(r'\$', replace, cmd)
234 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
233 return util.system(cmd, environ=env)
235 return util.system(cmd, environ=env)
234 self.fn = fn
236 self.fn = fn
235 return
237 return
236
238
237 args = shlex.split(self.definition)
239 args = shlex.split(self.definition)
238 self.cmdname = cmd = args.pop(0)
240 self.cmdname = cmd = args.pop(0)
239 args = map(util.expandpath, args)
241 args = map(util.expandpath, args)
240
242
241 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
243 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
242 if _earlygetopt([invalidarg], args):
244 if _earlygetopt([invalidarg], args):
243 def fn(ui, *args):
245 def fn(ui, *args):
244 ui.warn(_("error in definition for alias '%s': %s may only "
246 ui.warn(_("error in definition for alias '%s': %s may only "
245 "be given on the command line\n")
247 "be given on the command line\n")
246 % (self.name, invalidarg))
248 % (self.name, invalidarg))
247 return 1
249 return 1
248
250
249 self.fn = fn
251 self.fn = fn
250 self.badalias = True
252 self.badalias = True
251 return
253 return
252
254
253 try:
255 try:
254 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
256 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
255 if len(tableentry) > 2:
257 if len(tableentry) > 2:
256 self.fn, self.opts, self.help = tableentry
258 self.fn, self.opts, self.help = tableentry
257 else:
259 else:
258 self.fn, self.opts = tableentry
260 self.fn, self.opts = tableentry
259
261
260 self.args = aliasargs(self.fn) + args
262 self.args = aliasargs(self.fn) + args
261 if cmd not in commands.norepo.split(' '):
263 if cmd not in commands.norepo.split(' '):
262 self.norepo = False
264 self.norepo = False
263 if self.help.startswith("hg " + cmd):
265 if self.help.startswith("hg " + cmd):
264 # drop prefix in old-style help lines so hg shows the alias
266 # drop prefix in old-style help lines so hg shows the alias
265 self.help = self.help[4 + len(cmd):]
267 self.help = self.help[4 + len(cmd):]
266 self.__doc__ = self.fn.__doc__
268 self.__doc__ = self.fn.__doc__
267
269
268 except error.UnknownCommand:
270 except error.UnknownCommand:
269 def fn(ui, *args):
271 def fn(ui, *args):
270 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
272 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
271 % (self.name, cmd))
273 % (self.name, cmd))
272 try:
274 try:
273 # check if the command is in a disabled extension
275 # check if the command is in a disabled extension
274 commands.help_(ui, cmd, unknowncmd=True)
276 commands.help_(ui, cmd, unknowncmd=True)
275 except error.UnknownCommand:
277 except error.UnknownCommand:
276 pass
278 pass
277 return 1
279 return 1
278 self.fn = fn
280 self.fn = fn
279 self.badalias = True
281 self.badalias = True
280 except error.AmbiguousCommand:
282 except error.AmbiguousCommand:
281 def fn(ui, *args):
283 def fn(ui, *args):
282 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
284 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
283 % (self.name, cmd))
285 % (self.name, cmd))
284 return 1
286 return 1
285 self.fn = fn
287 self.fn = fn
286 self.badalias = True
288 self.badalias = True
287
289
288 def __call__(self, ui, *args, **opts):
290 def __call__(self, ui, *args, **opts):
289 if self.shadows:
291 if self.shadows:
290 ui.debug("alias '%s' shadows command '%s'\n" %
292 ui.debug("alias '%s' shadows command '%s'\n" %
291 (self.name, self.cmdname))
293 (self.name, self.cmdname))
292
294
293 if self.definition.startswith('!'):
295 if self.definition.startswith('!'):
294 return self.fn(ui, *args, **opts)
296 return self.fn(ui, *args, **opts)
295 else:
297 else:
296 try:
298 try:
297 util.checksignature(self.fn)(ui, *args, **opts)
299 util.checksignature(self.fn)(ui, *args, **opts)
298 except error.SignatureError:
300 except error.SignatureError:
299 args = ' '.join([self.cmdname] + self.args)
301 args = ' '.join([self.cmdname] + self.args)
300 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
302 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
301 raise
303 raise
302
304
303 def addaliases(ui, cmdtable):
305 def addaliases(ui, cmdtable):
304 # aliases are processed after extensions have been loaded, so they
306 # aliases are processed after extensions have been loaded, so they
305 # may use extension commands. Aliases can also use other alias definitions,
307 # may use extension commands. Aliases can also use other alias definitions,
306 # but only if they have been defined prior to the current definition.
308 # but only if they have been defined prior to the current definition.
307 for alias, definition in ui.configitems('alias'):
309 for alias, definition in ui.configitems('alias'):
308 aliasdef = cmdalias(alias, definition, cmdtable)
310 aliasdef = cmdalias(alias, definition, cmdtable)
309 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
311 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
310 if aliasdef.norepo:
312 if aliasdef.norepo:
311 commands.norepo += ' %s' % alias
313 commands.norepo += ' %s' % alias
312
314
313 def _parse(ui, args):
315 def _parse(ui, args):
314 options = {}
316 options = {}
315 cmdoptions = {}
317 cmdoptions = {}
316
318
317 try:
319 try:
318 args = fancyopts.fancyopts(args, commands.globalopts, options)
320 args = fancyopts.fancyopts(args, commands.globalopts, options)
319 except fancyopts.getopt.GetoptError, inst:
321 except fancyopts.getopt.GetoptError, inst:
320 raise error.CommandError(None, inst)
322 raise error.CommandError(None, inst)
321
323
322 if args:
324 if args:
323 cmd, args = args[0], args[1:]
325 cmd, args = args[0], args[1:]
324 aliases, entry = cmdutil.findcmd(cmd, commands.table,
326 aliases, entry = cmdutil.findcmd(cmd, commands.table,
325 ui.config("ui", "strict"))
327 ui.config("ui", "strict"))
326 cmd = aliases[0]
328 cmd = aliases[0]
327 args = aliasargs(entry[0]) + args
329 args = aliasargs(entry[0]) + args
328 defaults = ui.config("defaults", cmd)
330 defaults = ui.config("defaults", cmd)
329 if defaults:
331 if defaults:
330 args = map(util.expandpath, shlex.split(defaults)) + args
332 args = map(util.expandpath, shlex.split(defaults)) + args
331 c = list(entry[1])
333 c = list(entry[1])
332 else:
334 else:
333 cmd = None
335 cmd = None
334 c = []
336 c = []
335
337
336 # combine global options into local
338 # combine global options into local
337 for o in commands.globalopts:
339 for o in commands.globalopts:
338 c.append((o[0], o[1], options[o[1]], o[3]))
340 c.append((o[0], o[1], options[o[1]], o[3]))
339
341
340 try:
342 try:
341 args = fancyopts.fancyopts(args, c, cmdoptions, True)
343 args = fancyopts.fancyopts(args, c, cmdoptions, True)
342 except fancyopts.getopt.GetoptError, inst:
344 except fancyopts.getopt.GetoptError, inst:
343 raise error.CommandError(cmd, inst)
345 raise error.CommandError(cmd, inst)
344
346
345 # separate global options back out
347 # separate global options back out
346 for o in commands.globalopts:
348 for o in commands.globalopts:
347 n = o[1]
349 n = o[1]
348 options[n] = cmdoptions[n]
350 options[n] = cmdoptions[n]
349 del cmdoptions[n]
351 del cmdoptions[n]
350
352
351 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
353 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
352
354
353 def _parseconfig(ui, config):
355 def _parseconfig(ui, config):
354 """parse the --config options from the command line"""
356 """parse the --config options from the command line"""
355 for cfg in config:
357 for cfg in config:
356 try:
358 try:
357 name, value = cfg.split('=', 1)
359 name, value = cfg.split('=', 1)
358 section, name = name.split('.', 1)
360 section, name = name.split('.', 1)
359 if not section or not name:
361 if not section or not name:
360 raise IndexError
362 raise IndexError
361 ui.setconfig(section, name, value)
363 ui.setconfig(section, name, value)
362 except (IndexError, ValueError):
364 except (IndexError, ValueError):
363 raise util.Abort(_('malformed --config option: %r '
365 raise util.Abort(_('malformed --config option: %r '
364 '(use --config section.name=value)') % cfg)
366 '(use --config section.name=value)') % cfg)
365
367
366 def _earlygetopt(aliases, args):
368 def _earlygetopt(aliases, args):
367 """Return list of values for an option (or aliases).
369 """Return list of values for an option (or aliases).
368
370
369 The values are listed in the order they appear in args.
371 The values are listed in the order they appear in args.
370 The options and values are removed from args.
372 The options and values are removed from args.
371 """
373 """
372 try:
374 try:
373 argcount = args.index("--")
375 argcount = args.index("--")
374 except ValueError:
376 except ValueError:
375 argcount = len(args)
377 argcount = len(args)
376 shortopts = [opt for opt in aliases if len(opt) == 2]
378 shortopts = [opt for opt in aliases if len(opt) == 2]
377 values = []
379 values = []
378 pos = 0
380 pos = 0
379 while pos < argcount:
381 while pos < argcount:
380 if args[pos] in aliases:
382 if args[pos] in aliases:
381 if pos + 1 >= argcount:
383 if pos + 1 >= argcount:
382 # ignore and let getopt report an error if there is no value
384 # ignore and let getopt report an error if there is no value
383 break
385 break
384 del args[pos]
386 del args[pos]
385 values.append(args.pop(pos))
387 values.append(args.pop(pos))
386 argcount -= 2
388 argcount -= 2
387 elif args[pos][:2] in shortopts:
389 elif args[pos][:2] in shortopts:
388 # short option can have no following space, e.g. hg log -Rfoo
390 # short option can have no following space, e.g. hg log -Rfoo
389 values.append(args.pop(pos)[2:])
391 values.append(args.pop(pos)[2:])
390 argcount -= 1
392 argcount -= 1
391 else:
393 else:
392 pos += 1
394 pos += 1
393 return values
395 return values
394
396
395 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
397 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
396 # run pre-hook, and abort if it fails
398 # run pre-hook, and abort if it fails
397 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
399 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
398 pats=cmdpats, opts=cmdoptions)
400 pats=cmdpats, opts=cmdoptions)
399 if ret:
401 if ret:
400 return ret
402 return ret
401 ret = _runcommand(ui, options, cmd, d)
403 ret = _runcommand(ui, options, cmd, d)
402 # run post-hook, passing command result
404 # run post-hook, passing command result
403 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
405 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
404 result=ret, pats=cmdpats, opts=cmdoptions)
406 result=ret, pats=cmdpats, opts=cmdoptions)
405 return ret
407 return ret
406
408
407 def _getlocal(ui, rpath):
409 def _getlocal(ui, rpath):
408 """Return (path, local ui object) for the given target path.
410 """Return (path, local ui object) for the given target path.
409
411
410 Takes paths in [cwd]/.hg/hgrc into account."
412 Takes paths in [cwd]/.hg/hgrc into account."
411 """
413 """
412 try:
414 try:
413 wd = os.getcwd()
415 wd = os.getcwd()
414 except OSError, e:
416 except OSError, e:
415 raise util.Abort(_("error getting current working directory: %s") %
417 raise util.Abort(_("error getting current working directory: %s") %
416 e.strerror)
418 e.strerror)
417 path = cmdutil.findrepo(wd) or ""
419 path = cmdutil.findrepo(wd) or ""
418 if not path:
420 if not path:
419 lui = ui
421 lui = ui
420 else:
422 else:
421 lui = ui.copy()
423 lui = ui.copy()
422 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
424 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
423
425
424 if rpath:
426 if rpath:
425 path = lui.expandpath(rpath[-1])
427 path = lui.expandpath(rpath[-1])
426 lui = ui.copy()
428 lui = ui.copy()
427 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
429 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
428
430
429 return path, lui
431 return path, lui
430
432
431 def _checkshellalias(ui, args):
433 def _checkshellalias(ui, args):
432 cwd = os.getcwd()
434 cwd = os.getcwd()
433 norepo = commands.norepo
435 norepo = commands.norepo
434 options = {}
436 options = {}
435
437
436 try:
438 try:
437 args = fancyopts.fancyopts(args, commands.globalopts, options)
439 args = fancyopts.fancyopts(args, commands.globalopts, options)
438 except fancyopts.getopt.GetoptError:
440 except fancyopts.getopt.GetoptError:
439 return
441 return
440
442
441 if not args:
443 if not args:
442 return
444 return
443
445
444 _parseconfig(ui, options['config'])
446 _parseconfig(ui, options['config'])
445 if options['cwd']:
447 if options['cwd']:
446 os.chdir(options['cwd'])
448 os.chdir(options['cwd'])
447
449
448 path, lui = _getlocal(ui, [options['repository']])
450 path, lui = _getlocal(ui, [options['repository']])
449
451
450 cmdtable = commands.table.copy()
452 cmdtable = commands.table.copy()
451 addaliases(lui, cmdtable)
453 addaliases(lui, cmdtable)
452
454
453 cmd = args[0]
455 cmd = args[0]
454 try:
456 try:
455 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
457 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
456 except (error.AmbiguousCommand, error.UnknownCommand):
458 except (error.AmbiguousCommand, error.UnknownCommand):
457 commands.norepo = norepo
459 commands.norepo = norepo
458 os.chdir(cwd)
460 os.chdir(cwd)
459 return
461 return
460
462
461 cmd = aliases[0]
463 cmd = aliases[0]
462 fn = entry[0]
464 fn = entry[0]
463
465
464 if cmd and hasattr(fn, 'shell'):
466 if cmd and hasattr(fn, 'shell'):
465 d = lambda: fn(ui, *args[1:])
467 d = lambda: fn(ui, *args[1:])
466 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
468 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
467
469
468 commands.norepo = norepo
470 commands.norepo = norepo
469 os.chdir(cwd)
471 os.chdir(cwd)
470
472
471 _loaded = set()
473 _loaded = set()
472 def _dispatch(ui, args):
474 def _dispatch(ui, args):
473 shellaliasfn = _checkshellalias(ui, args)
475 shellaliasfn = _checkshellalias(ui, args)
474 if shellaliasfn:
476 if shellaliasfn:
475 return shellaliasfn()
477 return shellaliasfn()
476
478
477 # read --config before doing anything else
479 # read --config before doing anything else
478 # (e.g. to change trust settings for reading .hg/hgrc)
480 # (e.g. to change trust settings for reading .hg/hgrc)
479 _parseconfig(ui, _earlygetopt(['--config'], args))
481 _parseconfig(ui, _earlygetopt(['--config'], args))
480
482
481 # check for cwd
483 # check for cwd
482 cwd = _earlygetopt(['--cwd'], args)
484 cwd = _earlygetopt(['--cwd'], args)
483 if cwd:
485 if cwd:
484 os.chdir(cwd[-1])
486 os.chdir(cwd[-1])
485
487
486 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
488 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
487 path, lui = _getlocal(ui, rpath)
489 path, lui = _getlocal(ui, rpath)
488
490
489 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
491 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
490 # reposetup. Programs like TortoiseHg will call _dispatch several
492 # reposetup. Programs like TortoiseHg will call _dispatch several
491 # times so we keep track of configured extensions in _loaded.
493 # times so we keep track of configured extensions in _loaded.
492 extensions.loadall(lui)
494 extensions.loadall(lui)
493 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
495 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
494 # Propagate any changes to lui.__class__ by extensions
496 # Propagate any changes to lui.__class__ by extensions
495 ui.__class__ = lui.__class__
497 ui.__class__ = lui.__class__
496
498
497 # (uisetup and extsetup are handled in extensions.loadall)
499 # (uisetup and extsetup are handled in extensions.loadall)
498
500
499 for name, module in exts:
501 for name, module in exts:
500 cmdtable = getattr(module, 'cmdtable', {})
502 cmdtable = getattr(module, 'cmdtable', {})
501 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
503 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
502 if overrides:
504 if overrides:
503 ui.warn(_("extension '%s' overrides commands: %s\n")
505 ui.warn(_("extension '%s' overrides commands: %s\n")
504 % (name, " ".join(overrides)))
506 % (name, " ".join(overrides)))
505 commands.table.update(cmdtable)
507 commands.table.update(cmdtable)
506 _loaded.add(name)
508 _loaded.add(name)
507
509
508 # (reposetup is handled in hg.repository)
510 # (reposetup is handled in hg.repository)
509
511
510 addaliases(lui, commands.table)
512 addaliases(lui, commands.table)
511
513
512 # check for fallback encoding
514 # check for fallback encoding
513 fallback = lui.config('ui', 'fallbackencoding')
515 fallback = lui.config('ui', 'fallbackencoding')
514 if fallback:
516 if fallback:
515 encoding.fallbackencoding = fallback
517 encoding.fallbackencoding = fallback
516
518
517 fullargs = args
519 fullargs = args
518 cmd, func, args, options, cmdoptions = _parse(lui, args)
520 cmd, func, args, options, cmdoptions = _parse(lui, args)
519
521
520 if options["config"]:
522 if options["config"]:
521 raise util.Abort(_("option --config may not be abbreviated!"))
523 raise util.Abort(_("option --config may not be abbreviated!"))
522 if options["cwd"]:
524 if options["cwd"]:
523 raise util.Abort(_("option --cwd may not be abbreviated!"))
525 raise util.Abort(_("option --cwd may not be abbreviated!"))
524 if options["repository"]:
526 if options["repository"]:
525 raise util.Abort(_(
527 raise util.Abort(_(
526 "Option -R has to be separated from other options (e.g. not -qR) "
528 "Option -R has to be separated from other options (e.g. not -qR) "
527 "and --repository may only be abbreviated as --repo!"))
529 "and --repository may only be abbreviated as --repo!"))
528
530
529 if options["encoding"]:
531 if options["encoding"]:
530 encoding.encoding = options["encoding"]
532 encoding.encoding = options["encoding"]
531 if options["encodingmode"]:
533 if options["encodingmode"]:
532 encoding.encodingmode = options["encodingmode"]
534 encoding.encodingmode = options["encodingmode"]
533 if options["time"]:
535 if options["time"]:
534 def get_times():
536 def get_times():
535 t = os.times()
537 t = os.times()
536 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
538 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
537 t = (t[0], t[1], t[2], t[3], time.clock())
539 t = (t[0], t[1], t[2], t[3], time.clock())
538 return t
540 return t
539 s = get_times()
541 s = get_times()
540 def print_time():
542 def print_time():
541 t = get_times()
543 t = get_times()
542 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
544 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
543 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
545 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
544 atexit.register(print_time)
546 atexit.register(print_time)
545
547
546 if options['verbose'] or options['debug'] or options['quiet']:
548 if options['verbose'] or options['debug'] or options['quiet']:
547 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
549 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
548 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
550 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
549 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
551 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
550 if options['traceback']:
552 if options['traceback']:
551 ui.setconfig('ui', 'traceback', 'on')
553 ui.setconfig('ui', 'traceback', 'on')
552 if options['noninteractive']:
554 if options['noninteractive']:
553 ui.setconfig('ui', 'interactive', 'off')
555 ui.setconfig('ui', 'interactive', 'off')
554
556
555 if cmdoptions.get('insecure', False):
557 if cmdoptions.get('insecure', False):
556 ui.setconfig('web', 'cacerts', '')
558 ui.setconfig('web', 'cacerts', '')
557
559
558 if options['help']:
560 if options['help']:
559 return commands.help_(ui, cmd, options['version'])
561 return commands.help_(ui, cmd, options['version'])
560 elif options['version']:
562 elif options['version']:
561 return commands.version_(ui)
563 return commands.version_(ui)
562 elif not cmd:
564 elif not cmd:
563 return commands.help_(ui, 'shortlist')
565 return commands.help_(ui, 'shortlist')
564
566
565 repo = None
567 repo = None
566 cmdpats = args[:]
568 cmdpats = args[:]
567 if cmd not in commands.norepo.split():
569 if cmd not in commands.norepo.split():
568 try:
570 try:
569 repo = hg.repository(ui, path=path)
571 repo = hg.repository(ui, path=path)
570 ui = repo.ui
572 ui = repo.ui
571 if not repo.local():
573 if not repo.local():
572 raise util.Abort(_("repository '%s' is not local") % path)
574 raise util.Abort(_("repository '%s' is not local") % path)
573 ui.setconfig("bundle", "mainreporoot", repo.root)
575 ui.setconfig("bundle", "mainreporoot", repo.root)
574 except error.RepoError:
576 except error.RepoError:
575 if cmd not in commands.optionalrepo.split():
577 if cmd not in commands.optionalrepo.split():
576 if args and not path: # try to infer -R from command args
578 if args and not path: # try to infer -R from command args
577 repos = map(cmdutil.findrepo, args)
579 repos = map(cmdutil.findrepo, args)
578 guess = repos[0]
580 guess = repos[0]
579 if guess and repos.count(guess) == len(repos):
581 if guess and repos.count(guess) == len(repos):
580 return _dispatch(ui, ['--repository', guess] + fullargs)
582 return _dispatch(ui, ['--repository', guess] + fullargs)
581 if not path:
583 if not path:
582 raise error.RepoError(_("There is no Mercurial repository"
584 raise error.RepoError(_("There is no Mercurial repository"
583 " here (.hg not found)"))
585 " here (.hg not found)"))
584 raise
586 raise
585 args.insert(0, repo)
587 args.insert(0, repo)
586 elif rpath:
588 elif rpath:
587 ui.warn(_("warning: --repository ignored\n"))
589 ui.warn(_("warning: --repository ignored\n"))
588
590
589 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
591 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
590 ui.log("command", msg + "\n")
592 ui.log("command", msg + "\n")
591 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
593 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
592 try:
594 try:
593 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
595 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
594 cmdpats, cmdoptions)
596 cmdpats, cmdoptions)
595 finally:
597 finally:
596 if repo:
598 if repo:
597 repo.close()
599 repo.close()
598
600
599 def _runcommand(ui, options, cmd, cmdfunc):
601 def _runcommand(ui, options, cmd, cmdfunc):
600 def checkargs():
602 def checkargs():
601 try:
603 try:
602 return cmdfunc()
604 return cmdfunc()
603 except error.SignatureError:
605 except error.SignatureError:
604 raise error.CommandError(cmd, _("invalid arguments"))
606 raise error.CommandError(cmd, _("invalid arguments"))
605
607
606 if options['profile']:
608 if options['profile']:
607 format = ui.config('profiling', 'format', default='text')
609 format = ui.config('profiling', 'format', default='text')
608
610
609 if not format in ['text', 'kcachegrind']:
611 if not format in ['text', 'kcachegrind']:
610 ui.warn(_("unrecognized profiling format '%s'"
612 ui.warn(_("unrecognized profiling format '%s'"
611 " - Ignored\n") % format)
613 " - Ignored\n") % format)
612 format = 'text'
614 format = 'text'
613
615
614 output = ui.config('profiling', 'output')
616 output = ui.config('profiling', 'output')
615
617
616 if output:
618 if output:
617 path = ui.expandpath(output)
619 path = ui.expandpath(output)
618 ostream = open(path, 'wb')
620 ostream = open(path, 'wb')
619 else:
621 else:
620 ostream = sys.stderr
622 ostream = sys.stderr
621
623
622 try:
624 try:
623 from mercurial import lsprof
625 from mercurial import lsprof
624 except ImportError:
626 except ImportError:
625 raise util.Abort(_(
627 raise util.Abort(_(
626 'lsprof not available - install from '
628 'lsprof not available - install from '
627 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
629 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
628 p = lsprof.Profiler()
630 p = lsprof.Profiler()
629 p.enable(subcalls=True)
631 p.enable(subcalls=True)
630 try:
632 try:
631 return checkargs()
633 return checkargs()
632 finally:
634 finally:
633 p.disable()
635 p.disable()
634
636
635 if format == 'kcachegrind':
637 if format == 'kcachegrind':
636 import lsprofcalltree
638 import lsprofcalltree
637 calltree = lsprofcalltree.KCacheGrind(p)
639 calltree = lsprofcalltree.KCacheGrind(p)
638 calltree.output(ostream)
640 calltree.output(ostream)
639 else:
641 else:
640 # format == 'text'
642 # format == 'text'
641 stats = lsprof.Stats(p.getstats())
643 stats = lsprof.Stats(p.getstats())
642 stats.sort()
644 stats.sort()
643 stats.pprint(top=10, file=ostream, climit=5)
645 stats.pprint(top=10, file=ostream, climit=5)
644
646
645 if output:
647 if output:
646 ostream.close()
648 ostream.close()
647 else:
649 else:
648 return checkargs()
650 return checkargs()
@@ -1,1545 +1,1556 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, textwrap, unicodedata, signal
19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 import imp, socket
20 import imp, socket
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 if sys.version_info >= (2, 5):
31 if sys.version_info >= (2, 5):
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 else:
33 else:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import __builtin__
39 import __builtin__
40
40
41 if sys.version_info[0] < 3:
41 if sys.version_info[0] < 3:
42 def fakebuffer(sliceable, offset=0):
42 def fakebuffer(sliceable, offset=0):
43 return sliceable[offset:]
43 return sliceable[offset:]
44 else:
44 else:
45 def fakebuffer(sliceable, offset=0):
45 def fakebuffer(sliceable, offset=0):
46 return memoryview(sliceable)[offset:]
46 return memoryview(sliceable)[offset:]
47 try:
47 try:
48 buffer
48 buffer
49 except NameError:
49 except NameError:
50 __builtin__.buffer = fakebuffer
50 __builtin__.buffer = fakebuffer
51
51
52 import subprocess
52 import subprocess
53 closefds = os.name == 'posix'
53 closefds = os.name == 'posix'
54
54
55 def popen2(cmd, env=None, newlines=False):
55 def popen2(cmd, env=None, newlines=False):
56 # Setting bufsize to -1 lets the system decide the buffer size.
56 # Setting bufsize to -1 lets the system decide the buffer size.
57 # The default for bufsize is 0, meaning unbuffered. This leads to
57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 close_fds=closefds,
60 close_fds=closefds,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 universal_newlines=newlines,
62 universal_newlines=newlines,
63 env=env)
63 env=env)
64 return p.stdin, p.stdout
64 return p.stdin, p.stdout
65
65
66 def popen3(cmd, env=None, newlines=False):
66 def popen3(cmd, env=None, newlines=False):
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 close_fds=closefds,
68 close_fds=closefds,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 stderr=subprocess.PIPE,
70 stderr=subprocess.PIPE,
71 universal_newlines=newlines,
71 universal_newlines=newlines,
72 env=env)
72 env=env)
73 return p.stdin, p.stdout, p.stderr
73 return p.stdin, p.stdout, p.stderr
74
74
75 def version():
75 def version():
76 """Return version information if available."""
76 """Return version information if available."""
77 try:
77 try:
78 import __version__
78 import __version__
79 return __version__.version
79 return __version__.version
80 except ImportError:
80 except ImportError:
81 return 'unknown'
81 return 'unknown'
82
82
83 # used by parsedate
83 # used by parsedate
84 defaultdateformats = (
84 defaultdateformats = (
85 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %H:%M:%S',
86 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %I:%M:%S%p',
87 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %H:%M',
88 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d %I:%M%p',
89 '%Y-%m-%d',
89 '%Y-%m-%d',
90 '%m-%d',
90 '%m-%d',
91 '%m/%d',
91 '%m/%d',
92 '%m/%d/%y',
92 '%m/%d/%y',
93 '%m/%d/%Y',
93 '%m/%d/%Y',
94 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %H:%M:%S %Y',
95 '%a %b %d %I:%M:%S%p %Y',
95 '%a %b %d %I:%M:%S%p %Y',
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 '%b %d %H:%M:%S %Y',
97 '%b %d %H:%M:%S %Y',
98 '%b %d %I:%M:%S%p %Y',
98 '%b %d %I:%M:%S%p %Y',
99 '%b %d %H:%M:%S',
99 '%b %d %H:%M:%S',
100 '%b %d %I:%M:%S%p',
100 '%b %d %I:%M:%S%p',
101 '%b %d %H:%M',
101 '%b %d %H:%M',
102 '%b %d %I:%M%p',
102 '%b %d %I:%M%p',
103 '%b %d %Y',
103 '%b %d %Y',
104 '%b %d',
104 '%b %d',
105 '%H:%M:%S',
105 '%H:%M:%S',
106 '%I:%M:%S%p',
106 '%I:%M:%S%p',
107 '%H:%M',
107 '%H:%M',
108 '%I:%M%p',
108 '%I:%M%p',
109 )
109 )
110
110
111 extendeddateformats = defaultdateformats + (
111 extendeddateformats = defaultdateformats + (
112 "%Y",
112 "%Y",
113 "%Y-%m",
113 "%Y-%m",
114 "%b",
114 "%b",
115 "%b %Y",
115 "%b %Y",
116 )
116 )
117
117
118 def cachefunc(func):
118 def cachefunc(func):
119 '''cache the result of function calls'''
119 '''cache the result of function calls'''
120 # XXX doesn't handle keywords args
120 # XXX doesn't handle keywords args
121 cache = {}
121 cache = {}
122 if func.func_code.co_argcount == 1:
122 if func.func_code.co_argcount == 1:
123 # we gain a small amount of time because
123 # we gain a small amount of time because
124 # we don't need to pack/unpack the list
124 # we don't need to pack/unpack the list
125 def f(arg):
125 def f(arg):
126 if arg not in cache:
126 if arg not in cache:
127 cache[arg] = func(arg)
127 cache[arg] = func(arg)
128 return cache[arg]
128 return cache[arg]
129 else:
129 else:
130 def f(*args):
130 def f(*args):
131 if args not in cache:
131 if args not in cache:
132 cache[args] = func(*args)
132 cache[args] = func(*args)
133 return cache[args]
133 return cache[args]
134
134
135 return f
135 return f
136
136
137 def lrucachefunc(func):
137 def lrucachefunc(func):
138 '''cache most recent results of function calls'''
138 '''cache most recent results of function calls'''
139 cache = {}
139 cache = {}
140 order = []
140 order = []
141 if func.func_code.co_argcount == 1:
141 if func.func_code.co_argcount == 1:
142 def f(arg):
142 def f(arg):
143 if arg not in cache:
143 if arg not in cache:
144 if len(cache) > 20:
144 if len(cache) > 20:
145 del cache[order.pop(0)]
145 del cache[order.pop(0)]
146 cache[arg] = func(arg)
146 cache[arg] = func(arg)
147 else:
147 else:
148 order.remove(arg)
148 order.remove(arg)
149 order.append(arg)
149 order.append(arg)
150 return cache[arg]
150 return cache[arg]
151 else:
151 else:
152 def f(*args):
152 def f(*args):
153 if args not in cache:
153 if args not in cache:
154 if len(cache) > 20:
154 if len(cache) > 20:
155 del cache[order.pop(0)]
155 del cache[order.pop(0)]
156 cache[args] = func(*args)
156 cache[args] = func(*args)
157 else:
157 else:
158 order.remove(args)
158 order.remove(args)
159 order.append(args)
159 order.append(args)
160 return cache[args]
160 return cache[args]
161
161
162 return f
162 return f
163
163
164 class propertycache(object):
164 class propertycache(object):
165 def __init__(self, func):
165 def __init__(self, func):
166 self.func = func
166 self.func = func
167 self.name = func.__name__
167 self.name = func.__name__
168 def __get__(self, obj, type=None):
168 def __get__(self, obj, type=None):
169 result = self.func(obj)
169 result = self.func(obj)
170 setattr(obj, self.name, result)
170 setattr(obj, self.name, result)
171 return result
171 return result
172
172
173 def pipefilter(s, cmd):
173 def pipefilter(s, cmd):
174 '''filter string S through command CMD, returning its output'''
174 '''filter string S through command CMD, returning its output'''
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 pout, perr = p.communicate(s)
177 pout, perr = p.communicate(s)
178 return pout
178 return pout
179
179
180 def tempfilter(s, cmd):
180 def tempfilter(s, cmd):
181 '''filter string S through a pair of temporary files with CMD.
181 '''filter string S through a pair of temporary files with CMD.
182 CMD is used as a template to create the real command to be run,
182 CMD is used as a template to create the real command to be run,
183 with the strings INFILE and OUTFILE replaced by the real names of
183 with the strings INFILE and OUTFILE replaced by the real names of
184 the temporary files generated.'''
184 the temporary files generated.'''
185 inname, outname = None, None
185 inname, outname = None, None
186 try:
186 try:
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 fp = os.fdopen(infd, 'wb')
188 fp = os.fdopen(infd, 'wb')
189 fp.write(s)
189 fp.write(s)
190 fp.close()
190 fp.close()
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 os.close(outfd)
192 os.close(outfd)
193 cmd = cmd.replace('INFILE', inname)
193 cmd = cmd.replace('INFILE', inname)
194 cmd = cmd.replace('OUTFILE', outname)
194 cmd = cmd.replace('OUTFILE', outname)
195 code = os.system(cmd)
195 code = os.system(cmd)
196 if sys.platform == 'OpenVMS' and code & 1:
196 if sys.platform == 'OpenVMS' and code & 1:
197 code = 0
197 code = 0
198 if code:
198 if code:
199 raise Abort(_("command '%s' failed: %s") %
199 raise Abort(_("command '%s' failed: %s") %
200 (cmd, explain_exit(code)))
200 (cmd, explain_exit(code)))
201 return open(outname, 'rb').read()
201 return open(outname, 'rb').read()
202 finally:
202 finally:
203 try:
203 try:
204 if inname:
204 if inname:
205 os.unlink(inname)
205 os.unlink(inname)
206 except:
206 except:
207 pass
207 pass
208 try:
208 try:
209 if outname:
209 if outname:
210 os.unlink(outname)
210 os.unlink(outname)
211 except:
211 except:
212 pass
212 pass
213
213
214 filtertable = {
214 filtertable = {
215 'tempfile:': tempfilter,
215 'tempfile:': tempfilter,
216 'pipe:': pipefilter,
216 'pipe:': pipefilter,
217 }
217 }
218
218
219 def filter(s, cmd):
219 def filter(s, cmd):
220 "filter a string through a command that transforms its input to its output"
220 "filter a string through a command that transforms its input to its output"
221 for name, fn in filtertable.iteritems():
221 for name, fn in filtertable.iteritems():
222 if cmd.startswith(name):
222 if cmd.startswith(name):
223 return fn(s, cmd[len(name):].lstrip())
223 return fn(s, cmd[len(name):].lstrip())
224 return pipefilter(s, cmd)
224 return pipefilter(s, cmd)
225
225
226 def binary(s):
226 def binary(s):
227 """return true if a string is binary data"""
227 """return true if a string is binary data"""
228 return bool(s and '\0' in s)
228 return bool(s and '\0' in s)
229
229
230 def increasingchunks(source, min=1024, max=65536):
230 def increasingchunks(source, min=1024, max=65536):
231 '''return no less than min bytes per chunk while data remains,
231 '''return no less than min bytes per chunk while data remains,
232 doubling min after each chunk until it reaches max'''
232 doubling min after each chunk until it reaches max'''
233 def log2(x):
233 def log2(x):
234 if not x:
234 if not x:
235 return 0
235 return 0
236 i = 0
236 i = 0
237 while x:
237 while x:
238 x >>= 1
238 x >>= 1
239 i += 1
239 i += 1
240 return i - 1
240 return i - 1
241
241
242 buf = []
242 buf = []
243 blen = 0
243 blen = 0
244 for chunk in source:
244 for chunk in source:
245 buf.append(chunk)
245 buf.append(chunk)
246 blen += len(chunk)
246 blen += len(chunk)
247 if blen >= min:
247 if blen >= min:
248 if min < max:
248 if min < max:
249 min = min << 1
249 min = min << 1
250 nmin = 1 << log2(blen)
250 nmin = 1 << log2(blen)
251 if nmin > min:
251 if nmin > min:
252 min = nmin
252 min = nmin
253 if min > max:
253 if min > max:
254 min = max
254 min = max
255 yield ''.join(buf)
255 yield ''.join(buf)
256 blen = 0
256 blen = 0
257 buf = []
257 buf = []
258 if buf:
258 if buf:
259 yield ''.join(buf)
259 yield ''.join(buf)
260
260
261 Abort = error.Abort
261 Abort = error.Abort
262
262
263 def always(fn):
263 def always(fn):
264 return True
264 return True
265
265
266 def never(fn):
266 def never(fn):
267 return False
267 return False
268
268
269 def pathto(root, n1, n2):
269 def pathto(root, n1, n2):
270 '''return the relative path from one place to another.
270 '''return the relative path from one place to another.
271 root should use os.sep to separate directories
271 root should use os.sep to separate directories
272 n1 should use os.sep to separate directories
272 n1 should use os.sep to separate directories
273 n2 should use "/" to separate directories
273 n2 should use "/" to separate directories
274 returns an os.sep-separated path.
274 returns an os.sep-separated path.
275
275
276 If n1 is a relative path, it's assumed it's
276 If n1 is a relative path, it's assumed it's
277 relative to root.
277 relative to root.
278 n2 should always be relative to root.
278 n2 should always be relative to root.
279 '''
279 '''
280 if not n1:
280 if not n1:
281 return localpath(n2)
281 return localpath(n2)
282 if os.path.isabs(n1):
282 if os.path.isabs(n1):
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 return os.path.join(root, localpath(n2))
284 return os.path.join(root, localpath(n2))
285 n2 = '/'.join((pconvert(root), n2))
285 n2 = '/'.join((pconvert(root), n2))
286 a, b = splitpath(n1), n2.split('/')
286 a, b = splitpath(n1), n2.split('/')
287 a.reverse()
287 a.reverse()
288 b.reverse()
288 b.reverse()
289 while a and b and a[-1] == b[-1]:
289 while a and b and a[-1] == b[-1]:
290 a.pop()
290 a.pop()
291 b.pop()
291 b.pop()
292 b.reverse()
292 b.reverse()
293 return os.sep.join((['..'] * len(a)) + b) or '.'
293 return os.sep.join((['..'] * len(a)) + b) or '.'
294
294
295 def canonpath(root, cwd, myname, auditor=None):
295 def canonpath(root, cwd, myname, auditor=None):
296 """return the canonical path of myname, given cwd and root"""
296 """return the canonical path of myname, given cwd and root"""
297 if endswithsep(root):
297 if endswithsep(root):
298 rootsep = root
298 rootsep = root
299 else:
299 else:
300 rootsep = root + os.sep
300 rootsep = root + os.sep
301 name = myname
301 name = myname
302 if not os.path.isabs(name):
302 if not os.path.isabs(name):
303 name = os.path.join(root, cwd, name)
303 name = os.path.join(root, cwd, name)
304 name = os.path.normpath(name)
304 name = os.path.normpath(name)
305 if auditor is None:
305 if auditor is None:
306 auditor = path_auditor(root)
306 auditor = path_auditor(root)
307 if name != rootsep and name.startswith(rootsep):
307 if name != rootsep and name.startswith(rootsep):
308 name = name[len(rootsep):]
308 name = name[len(rootsep):]
309 auditor(name)
309 auditor(name)
310 return pconvert(name)
310 return pconvert(name)
311 elif name == root:
311 elif name == root:
312 return ''
312 return ''
313 else:
313 else:
314 # Determine whether `name' is in the hierarchy at or beneath `root',
314 # Determine whether `name' is in the hierarchy at or beneath `root',
315 # by iterating name=dirname(name) until that causes no change (can't
315 # by iterating name=dirname(name) until that causes no change (can't
316 # check name == '/', because that doesn't work on windows). For each
316 # check name == '/', because that doesn't work on windows). For each
317 # `name', compare dev/inode numbers. If they match, the list `rel'
317 # `name', compare dev/inode numbers. If they match, the list `rel'
318 # holds the reversed list of components making up the relative file
318 # holds the reversed list of components making up the relative file
319 # name we want.
319 # name we want.
320 root_st = os.stat(root)
320 root_st = os.stat(root)
321 rel = []
321 rel = []
322 while True:
322 while True:
323 try:
323 try:
324 name_st = os.stat(name)
324 name_st = os.stat(name)
325 except OSError:
325 except OSError:
326 break
326 break
327 if samestat(name_st, root_st):
327 if samestat(name_st, root_st):
328 if not rel:
328 if not rel:
329 # name was actually the same as root (maybe a symlink)
329 # name was actually the same as root (maybe a symlink)
330 return ''
330 return ''
331 rel.reverse()
331 rel.reverse()
332 name = os.path.join(*rel)
332 name = os.path.join(*rel)
333 auditor(name)
333 auditor(name)
334 return pconvert(name)
334 return pconvert(name)
335 dirname, basename = os.path.split(name)
335 dirname, basename = os.path.split(name)
336 rel.append(basename)
336 rel.append(basename)
337 if dirname == name:
337 if dirname == name:
338 break
338 break
339 name = dirname
339 name = dirname
340
340
341 raise Abort('%s not under root' % myname)
341 raise Abort('%s not under root' % myname)
342
342
343 _hgexecutable = None
343 _hgexecutable = None
344
344
345 def main_is_frozen():
345 def main_is_frozen():
346 """return True if we are a frozen executable.
346 """return True if we are a frozen executable.
347
347
348 The code supports py2exe (most common, Windows only) and tools/freeze
348 The code supports py2exe (most common, Windows only) and tools/freeze
349 (portable, not much used).
349 (portable, not much used).
350 """
350 """
351 return (hasattr(sys, "frozen") or # new py2exe
351 return (hasattr(sys, "frozen") or # new py2exe
352 hasattr(sys, "importers") or # old py2exe
352 hasattr(sys, "importers") or # old py2exe
353 imp.is_frozen("__main__")) # tools/freeze
353 imp.is_frozen("__main__")) # tools/freeze
354
354
355 def hgexecutable():
355 def hgexecutable():
356 """return location of the 'hg' executable.
356 """return location of the 'hg' executable.
357
357
358 Defaults to $HG or 'hg' in the search path.
358 Defaults to $HG or 'hg' in the search path.
359 """
359 """
360 if _hgexecutable is None:
360 if _hgexecutable is None:
361 hg = os.environ.get('HG')
361 hg = os.environ.get('HG')
362 if hg:
362 if hg:
363 set_hgexecutable(hg)
363 set_hgexecutable(hg)
364 elif main_is_frozen():
364 elif main_is_frozen():
365 set_hgexecutable(sys.executable)
365 set_hgexecutable(sys.executable)
366 else:
366 else:
367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
368 set_hgexecutable(exe)
368 set_hgexecutable(exe)
369 return _hgexecutable
369 return _hgexecutable
370
370
371 def set_hgexecutable(path):
371 def set_hgexecutable(path):
372 """set location of the 'hg' executable"""
372 """set location of the 'hg' executable"""
373 global _hgexecutable
373 global _hgexecutable
374 _hgexecutable = path
374 _hgexecutable = path
375
375
376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
377 '''enhanced shell command execution.
377 '''enhanced shell command execution.
378 run with environment maybe modified, maybe in different dir.
378 run with environment maybe modified, maybe in different dir.
379
379
380 if command fails and onerr is None, return status. if ui object,
380 if command fails and onerr is None, return status. if ui object,
381 print error message and return status, else raise onerr object as
381 print error message and return status, else raise onerr object as
382 exception.
382 exception.
383
383
384 if out is specified, it is assumed to be a file-like object that has a
384 if out is specified, it is assumed to be a file-like object that has a
385 write() method. stdout and stderr will be redirected to out.'''
385 write() method. stdout and stderr will be redirected to out.'''
386 def py2shell(val):
386 def py2shell(val):
387 'convert python object into string that is useful to shell'
387 'convert python object into string that is useful to shell'
388 if val is None or val is False:
388 if val is None or val is False:
389 return '0'
389 return '0'
390 if val is True:
390 if val is True:
391 return '1'
391 return '1'
392 return str(val)
392 return str(val)
393 origcmd = cmd
393 origcmd = cmd
394 cmd = quotecommand(cmd)
394 cmd = quotecommand(cmd)
395 env = dict(os.environ)
395 env = dict(os.environ)
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
397 env['HG'] = hgexecutable()
397 env['HG'] = hgexecutable()
398 if out is None:
398 if out is None:
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
400 env=env, cwd=cwd)
400 env=env, cwd=cwd)
401 else:
401 else:
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
404 stderr=subprocess.STDOUT)
404 stderr=subprocess.STDOUT)
405 for line in proc.stdout:
405 for line in proc.stdout:
406 out.write(line)
406 out.write(line)
407 proc.wait()
407 proc.wait()
408 rc = proc.returncode
408 rc = proc.returncode
409 if sys.platform == 'OpenVMS' and rc & 1:
409 if sys.platform == 'OpenVMS' and rc & 1:
410 rc = 0
410 rc = 0
411 if rc and onerr:
411 if rc and onerr:
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
413 explain_exit(rc)[0])
413 explain_exit(rc)[0])
414 if errprefix:
414 if errprefix:
415 errmsg = '%s: %s' % (errprefix, errmsg)
415 errmsg = '%s: %s' % (errprefix, errmsg)
416 try:
416 try:
417 onerr.warn(errmsg + '\n')
417 onerr.warn(errmsg + '\n')
418 except AttributeError:
418 except AttributeError:
419 raise onerr(errmsg)
419 raise onerr(errmsg)
420 return rc
420 return rc
421
421
422 def checksignature(func):
422 def checksignature(func):
423 '''wrap a function with code to check for calling errors'''
423 '''wrap a function with code to check for calling errors'''
424 def check(*args, **kwargs):
424 def check(*args, **kwargs):
425 try:
425 try:
426 return func(*args, **kwargs)
426 return func(*args, **kwargs)
427 except TypeError:
427 except TypeError:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
429 raise error.SignatureError
429 raise error.SignatureError
430 raise
430 raise
431
431
432 return check
432 return check
433
433
434 def unlinkpath(f):
434 def unlinkpath(f):
435 """unlink and remove the directory if it is empty"""
435 """unlink and remove the directory if it is empty"""
436 os.unlink(f)
436 os.unlink(f)
437 # try removing directories that might now be empty
437 # try removing directories that might now be empty
438 try:
438 try:
439 os.removedirs(os.path.dirname(f))
439 os.removedirs(os.path.dirname(f))
440 except OSError:
440 except OSError:
441 pass
441 pass
442
442
443 def copyfile(src, dest):
443 def copyfile(src, dest):
444 "copy a file, preserving mode and atime/mtime"
444 "copy a file, preserving mode and atime/mtime"
445 if os.path.islink(src):
445 if os.path.islink(src):
446 try:
446 try:
447 os.unlink(dest)
447 os.unlink(dest)
448 except:
448 except:
449 pass
449 pass
450 os.symlink(os.readlink(src), dest)
450 os.symlink(os.readlink(src), dest)
451 else:
451 else:
452 try:
452 try:
453 shutil.copyfile(src, dest)
453 shutil.copyfile(src, dest)
454 shutil.copymode(src, dest)
454 shutil.copymode(src, dest)
455 except shutil.Error, inst:
455 except shutil.Error, inst:
456 raise Abort(str(inst))
456 raise Abort(str(inst))
457
457
458 def copyfiles(src, dst, hardlink=None):
458 def copyfiles(src, dst, hardlink=None):
459 """Copy a directory tree using hardlinks if possible"""
459 """Copy a directory tree using hardlinks if possible"""
460
460
461 if hardlink is None:
461 if hardlink is None:
462 hardlink = (os.stat(src).st_dev ==
462 hardlink = (os.stat(src).st_dev ==
463 os.stat(os.path.dirname(dst)).st_dev)
463 os.stat(os.path.dirname(dst)).st_dev)
464
464
465 num = 0
465 num = 0
466 if os.path.isdir(src):
466 if os.path.isdir(src):
467 os.mkdir(dst)
467 os.mkdir(dst)
468 for name, kind in osutil.listdir(src):
468 for name, kind in osutil.listdir(src):
469 srcname = os.path.join(src, name)
469 srcname = os.path.join(src, name)
470 dstname = os.path.join(dst, name)
470 dstname = os.path.join(dst, name)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
472 num += n
472 num += n
473 else:
473 else:
474 if hardlink:
474 if hardlink:
475 try:
475 try:
476 os_link(src, dst)
476 os_link(src, dst)
477 except (IOError, OSError):
477 except (IOError, OSError):
478 hardlink = False
478 hardlink = False
479 shutil.copy(src, dst)
479 shutil.copy(src, dst)
480 else:
480 else:
481 shutil.copy(src, dst)
481 shutil.copy(src, dst)
482 num += 1
482 num += 1
483
483
484 return hardlink, num
484 return hardlink, num
485
485
486 class path_auditor(object):
486 class path_auditor(object):
487 '''ensure that a filesystem path contains no banned components.
487 '''ensure that a filesystem path contains no banned components.
488 the following properties of a path are checked:
488 the following properties of a path are checked:
489
489
490 - ends with a directory separator
490 - ends with a directory separator
491 - under top-level .hg
491 - under top-level .hg
492 - starts at the root of a windows drive
492 - starts at the root of a windows drive
493 - contains ".."
493 - contains ".."
494 - traverses a symlink (e.g. a/symlink_here/b)
494 - traverses a symlink (e.g. a/symlink_here/b)
495 - inside a nested repository (a callback can be used to approve
495 - inside a nested repository (a callback can be used to approve
496 some nested repositories, e.g., subrepositories)
496 some nested repositories, e.g., subrepositories)
497 '''
497 '''
498
498
499 def __init__(self, root, callback=None):
499 def __init__(self, root, callback=None):
500 self.audited = set()
500 self.audited = set()
501 self.auditeddir = set()
501 self.auditeddir = set()
502 self.root = root
502 self.root = root
503 self.callback = callback
503 self.callback = callback
504
504
505 def __call__(self, path):
505 def __call__(self, path):
506 if path in self.audited:
506 if path in self.audited:
507 return
507 return
508 # AIX ignores "/" at end of path, others raise EISDIR.
508 # AIX ignores "/" at end of path, others raise EISDIR.
509 if endswithsep(path):
509 if endswithsep(path):
510 raise Abort(_("path ends in directory separator: %s") % path)
510 raise Abort(_("path ends in directory separator: %s") % path)
511 normpath = os.path.normcase(path)
511 normpath = os.path.normcase(path)
512 parts = splitpath(normpath)
512 parts = splitpath(normpath)
513 if (os.path.splitdrive(path)[0]
513 if (os.path.splitdrive(path)[0]
514 or parts[0].lower() in ('.hg', '.hg.', '')
514 or parts[0].lower() in ('.hg', '.hg.', '')
515 or os.pardir in parts):
515 or os.pardir in parts):
516 raise Abort(_("path contains illegal component: %s") % path)
516 raise Abort(_("path contains illegal component: %s") % path)
517 if '.hg' in path.lower():
517 if '.hg' in path.lower():
518 lparts = [p.lower() for p in parts]
518 lparts = [p.lower() for p in parts]
519 for p in '.hg', '.hg.':
519 for p in '.hg', '.hg.':
520 if p in lparts[1:]:
520 if p in lparts[1:]:
521 pos = lparts.index(p)
521 pos = lparts.index(p)
522 base = os.path.join(*parts[:pos])
522 base = os.path.join(*parts[:pos])
523 raise Abort(_('path %r is inside repo %r') % (path, base))
523 raise Abort(_('path %r is inside repo %r') % (path, base))
524 def check(prefix):
524 def check(prefix):
525 curpath = os.path.join(self.root, prefix)
525 curpath = os.path.join(self.root, prefix)
526 try:
526 try:
527 st = os.lstat(curpath)
527 st = os.lstat(curpath)
528 except OSError, err:
528 except OSError, err:
529 # EINVAL can be raised as invalid path syntax under win32.
529 # EINVAL can be raised as invalid path syntax under win32.
530 # They must be ignored for patterns can be checked too.
530 # They must be ignored for patterns can be checked too.
531 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
531 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
532 raise
532 raise
533 else:
533 else:
534 if stat.S_ISLNK(st.st_mode):
534 if stat.S_ISLNK(st.st_mode):
535 raise Abort(_('path %r traverses symbolic link %r') %
535 raise Abort(_('path %r traverses symbolic link %r') %
536 (path, prefix))
536 (path, prefix))
537 elif (stat.S_ISDIR(st.st_mode) and
537 elif (stat.S_ISDIR(st.st_mode) and
538 os.path.isdir(os.path.join(curpath, '.hg'))):
538 os.path.isdir(os.path.join(curpath, '.hg'))):
539 if not self.callback or not self.callback(curpath):
539 if not self.callback or not self.callback(curpath):
540 raise Abort(_('path %r is inside repo %r') %
540 raise Abort(_('path %r is inside repo %r') %
541 (path, prefix))
541 (path, prefix))
542 parts.pop()
542 parts.pop()
543 prefixes = []
543 prefixes = []
544 while parts:
544 while parts:
545 prefix = os.sep.join(parts)
545 prefix = os.sep.join(parts)
546 if prefix in self.auditeddir:
546 if prefix in self.auditeddir:
547 break
547 break
548 check(prefix)
548 check(prefix)
549 prefixes.append(prefix)
549 prefixes.append(prefix)
550 parts.pop()
550 parts.pop()
551
551
552 self.audited.add(path)
552 self.audited.add(path)
553 # only add prefixes to the cache after checking everything: we don't
553 # only add prefixes to the cache after checking everything: we don't
554 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
554 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
555 self.auditeddir.update(prefixes)
555 self.auditeddir.update(prefixes)
556
556
557 def lookup_reg(key, name=None, scope=None):
557 def lookup_reg(key, name=None, scope=None):
558 return None
558 return None
559
559
560 def hidewindow():
560 def hidewindow():
561 """Hide current shell window.
561 """Hide current shell window.
562
562
563 Used to hide the window opened when starting asynchronous
563 Used to hide the window opened when starting asynchronous
564 child process under Windows, unneeded on other systems.
564 child process under Windows, unneeded on other systems.
565 """
565 """
566 pass
566 pass
567
567
568 if os.name == 'nt':
568 if os.name == 'nt':
569 from windows import *
569 from windows import *
570 else:
570 else:
571 from posix import *
571 from posix import *
572
572
573 def makelock(info, pathname):
573 def makelock(info, pathname):
574 try:
574 try:
575 return os.symlink(info, pathname)
575 return os.symlink(info, pathname)
576 except OSError, why:
576 except OSError, why:
577 if why.errno == errno.EEXIST:
577 if why.errno == errno.EEXIST:
578 raise
578 raise
579 except AttributeError: # no symlink in os
579 except AttributeError: # no symlink in os
580 pass
580 pass
581
581
582 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
582 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
583 os.write(ld, info)
583 os.write(ld, info)
584 os.close(ld)
584 os.close(ld)
585
585
586 def readlock(pathname):
586 def readlock(pathname):
587 try:
587 try:
588 return os.readlink(pathname)
588 return os.readlink(pathname)
589 except OSError, why:
589 except OSError, why:
590 if why.errno not in (errno.EINVAL, errno.ENOSYS):
590 if why.errno not in (errno.EINVAL, errno.ENOSYS):
591 raise
591 raise
592 except AttributeError: # no symlink in os
592 except AttributeError: # no symlink in os
593 pass
593 pass
594 return posixfile(pathname).read()
594 return posixfile(pathname).read()
595
595
596 def fstat(fp):
596 def fstat(fp):
597 '''stat file object that may not have fileno method.'''
597 '''stat file object that may not have fileno method.'''
598 try:
598 try:
599 return os.fstat(fp.fileno())
599 return os.fstat(fp.fileno())
600 except AttributeError:
600 except AttributeError:
601 return os.stat(fp.name)
601 return os.stat(fp.name)
602
602
603 # File system features
603 # File system features
604
604
605 def checkcase(path):
605 def checkcase(path):
606 """
606 """
607 Check whether the given path is on a case-sensitive filesystem
607 Check whether the given path is on a case-sensitive filesystem
608
608
609 Requires a path (like /foo/.hg) ending with a foldable final
609 Requires a path (like /foo/.hg) ending with a foldable final
610 directory component.
610 directory component.
611 """
611 """
612 s1 = os.stat(path)
612 s1 = os.stat(path)
613 d, b = os.path.split(path)
613 d, b = os.path.split(path)
614 p2 = os.path.join(d, b.upper())
614 p2 = os.path.join(d, b.upper())
615 if path == p2:
615 if path == p2:
616 p2 = os.path.join(d, b.lower())
616 p2 = os.path.join(d, b.lower())
617 try:
617 try:
618 s2 = os.stat(p2)
618 s2 = os.stat(p2)
619 if s2 == s1:
619 if s2 == s1:
620 return False
620 return False
621 return True
621 return True
622 except:
622 except:
623 return True
623 return True
624
624
625 _fspathcache = {}
625 _fspathcache = {}
626 def fspath(name, root):
626 def fspath(name, root):
627 '''Get name in the case stored in the filesystem
627 '''Get name in the case stored in the filesystem
628
628
629 The name is either relative to root, or it is an absolute path starting
629 The name is either relative to root, or it is an absolute path starting
630 with root. Note that this function is unnecessary, and should not be
630 with root. Note that this function is unnecessary, and should not be
631 called, for case-sensitive filesystems (simply because it's expensive).
631 called, for case-sensitive filesystems (simply because it's expensive).
632 '''
632 '''
633 # If name is absolute, make it relative
633 # If name is absolute, make it relative
634 if name.lower().startswith(root.lower()):
634 if name.lower().startswith(root.lower()):
635 l = len(root)
635 l = len(root)
636 if name[l] == os.sep or name[l] == os.altsep:
636 if name[l] == os.sep or name[l] == os.altsep:
637 l = l + 1
637 l = l + 1
638 name = name[l:]
638 name = name[l:]
639
639
640 if not os.path.lexists(os.path.join(root, name)):
640 if not os.path.lexists(os.path.join(root, name)):
641 return None
641 return None
642
642
643 seps = os.sep
643 seps = os.sep
644 if os.altsep:
644 if os.altsep:
645 seps = seps + os.altsep
645 seps = seps + os.altsep
646 # Protect backslashes. This gets silly very quickly.
646 # Protect backslashes. This gets silly very quickly.
647 seps.replace('\\','\\\\')
647 seps.replace('\\','\\\\')
648 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
648 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
649 dir = os.path.normcase(os.path.normpath(root))
649 dir = os.path.normcase(os.path.normpath(root))
650 result = []
650 result = []
651 for part, sep in pattern.findall(name):
651 for part, sep in pattern.findall(name):
652 if sep:
652 if sep:
653 result.append(sep)
653 result.append(sep)
654 continue
654 continue
655
655
656 if dir not in _fspathcache:
656 if dir not in _fspathcache:
657 _fspathcache[dir] = os.listdir(dir)
657 _fspathcache[dir] = os.listdir(dir)
658 contents = _fspathcache[dir]
658 contents = _fspathcache[dir]
659
659
660 lpart = part.lower()
660 lpart = part.lower()
661 lenp = len(part)
661 lenp = len(part)
662 for n in contents:
662 for n in contents:
663 if lenp == len(n) and n.lower() == lpart:
663 if lenp == len(n) and n.lower() == lpart:
664 result.append(n)
664 result.append(n)
665 break
665 break
666 else:
666 else:
667 # Cannot happen, as the file exists!
667 # Cannot happen, as the file exists!
668 result.append(part)
668 result.append(part)
669 dir = os.path.join(dir, lpart)
669 dir = os.path.join(dir, lpart)
670
670
671 return ''.join(result)
671 return ''.join(result)
672
672
673 def checkexec(path):
673 def checkexec(path):
674 """
674 """
675 Check whether the given path is on a filesystem with UNIX-like exec flags
675 Check whether the given path is on a filesystem with UNIX-like exec flags
676
676
677 Requires a directory (like /foo/.hg)
677 Requires a directory (like /foo/.hg)
678 """
678 """
679
679
680 # VFAT on some Linux versions can flip mode but it doesn't persist
680 # VFAT on some Linux versions can flip mode but it doesn't persist
681 # a FS remount. Frequently we can detect it if files are created
681 # a FS remount. Frequently we can detect it if files are created
682 # with exec bit on.
682 # with exec bit on.
683
683
684 try:
684 try:
685 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
685 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
686 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
686 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
687 try:
687 try:
688 os.close(fh)
688 os.close(fh)
689 m = os.stat(fn).st_mode & 0777
689 m = os.stat(fn).st_mode & 0777
690 new_file_has_exec = m & EXECFLAGS
690 new_file_has_exec = m & EXECFLAGS
691 os.chmod(fn, m ^ EXECFLAGS)
691 os.chmod(fn, m ^ EXECFLAGS)
692 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
692 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
693 finally:
693 finally:
694 os.unlink(fn)
694 os.unlink(fn)
695 except (IOError, OSError):
695 except (IOError, OSError):
696 # we don't care, the user probably won't be able to commit anyway
696 # we don't care, the user probably won't be able to commit anyway
697 return False
697 return False
698 return not (new_file_has_exec or exec_flags_cannot_flip)
698 return not (new_file_has_exec or exec_flags_cannot_flip)
699
699
700 def checklink(path):
700 def checklink(path):
701 """check whether the given path is on a symlink-capable filesystem"""
701 """check whether the given path is on a symlink-capable filesystem"""
702 # mktemp is not racy because symlink creation will fail if the
702 # mktemp is not racy because symlink creation will fail if the
703 # file already exists
703 # file already exists
704 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
704 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
705 try:
705 try:
706 os.symlink(".", name)
706 os.symlink(".", name)
707 os.unlink(name)
707 os.unlink(name)
708 return True
708 return True
709 except (OSError, AttributeError):
709 except (OSError, AttributeError):
710 return False
710 return False
711
711
712 def checknlink(testfile):
712 def checknlink(testfile):
713 '''check whether hardlink count reporting works properly'''
713 '''check whether hardlink count reporting works properly'''
714
714
715 # testfile may be open, so we need a separate file for checking to
715 # testfile may be open, so we need a separate file for checking to
716 # work around issue2543 (or testfile may get lost on Samba shares)
716 # work around issue2543 (or testfile may get lost on Samba shares)
717 f1 = testfile + ".hgtmp1"
717 f1 = testfile + ".hgtmp1"
718 if os.path.lexists(f1):
718 if os.path.lexists(f1):
719 return False
719 return False
720 try:
720 try:
721 posixfile(f1, 'w').close()
721 posixfile(f1, 'w').close()
722 except IOError:
722 except IOError:
723 return False
723 return False
724
724
725 f2 = testfile + ".hgtmp2"
725 f2 = testfile + ".hgtmp2"
726 fd = None
726 fd = None
727 try:
727 try:
728 try:
728 try:
729 os_link(f1, f2)
729 os_link(f1, f2)
730 except OSError:
730 except OSError:
731 return False
731 return False
732
732
733 # nlinks() may behave differently for files on Windows shares if
733 # nlinks() may behave differently for files on Windows shares if
734 # the file is open.
734 # the file is open.
735 fd = posixfile(f2)
735 fd = posixfile(f2)
736 return nlinks(f2) > 1
736 return nlinks(f2) > 1
737 finally:
737 finally:
738 if fd is not None:
738 if fd is not None:
739 fd.close()
739 fd.close()
740 for f in (f1, f2):
740 for f in (f1, f2):
741 try:
741 try:
742 os.unlink(f)
742 os.unlink(f)
743 except OSError:
743 except OSError:
744 pass
744 pass
745
745
746 return False
746 return False
747
747
748 def endswithsep(path):
748 def endswithsep(path):
749 '''Check path ends with os.sep or os.altsep.'''
749 '''Check path ends with os.sep or os.altsep.'''
750 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
750 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
751
751
752 def splitpath(path):
752 def splitpath(path):
753 '''Split path by os.sep.
753 '''Split path by os.sep.
754 Note that this function does not use os.altsep because this is
754 Note that this function does not use os.altsep because this is
755 an alternative of simple "xxx.split(os.sep)".
755 an alternative of simple "xxx.split(os.sep)".
756 It is recommended to use os.path.normpath() before using this
756 It is recommended to use os.path.normpath() before using this
757 function if need.'''
757 function if need.'''
758 return path.split(os.sep)
758 return path.split(os.sep)
759
759
760 def gui():
760 def gui():
761 '''Are we running in a GUI?'''
761 '''Are we running in a GUI?'''
762 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
762 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
763
763
764 def mktempcopy(name, emptyok=False, createmode=None):
764 def mktempcopy(name, emptyok=False, createmode=None):
765 """Create a temporary file with the same contents from name
765 """Create a temporary file with the same contents from name
766
766
767 The permission bits are copied from the original file.
767 The permission bits are copied from the original file.
768
768
769 If the temporary file is going to be truncated immediately, you
769 If the temporary file is going to be truncated immediately, you
770 can use emptyok=True as an optimization.
770 can use emptyok=True as an optimization.
771
771
772 Returns the name of the temporary file.
772 Returns the name of the temporary file.
773 """
773 """
774 d, fn = os.path.split(name)
774 d, fn = os.path.split(name)
775 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
775 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
776 os.close(fd)
776 os.close(fd)
777 # Temporary files are created with mode 0600, which is usually not
777 # Temporary files are created with mode 0600, which is usually not
778 # what we want. If the original file already exists, just copy
778 # what we want. If the original file already exists, just copy
779 # its mode. Otherwise, manually obey umask.
779 # its mode. Otherwise, manually obey umask.
780 try:
780 try:
781 st_mode = os.lstat(name).st_mode & 0777
781 st_mode = os.lstat(name).st_mode & 0777
782 except OSError, inst:
782 except OSError, inst:
783 if inst.errno != errno.ENOENT:
783 if inst.errno != errno.ENOENT:
784 raise
784 raise
785 st_mode = createmode
785 st_mode = createmode
786 if st_mode is None:
786 if st_mode is None:
787 st_mode = ~umask
787 st_mode = ~umask
788 st_mode &= 0666
788 st_mode &= 0666
789 os.chmod(temp, st_mode)
789 os.chmod(temp, st_mode)
790 if emptyok:
790 if emptyok:
791 return temp
791 return temp
792 try:
792 try:
793 try:
793 try:
794 ifp = posixfile(name, "rb")
794 ifp = posixfile(name, "rb")
795 except IOError, inst:
795 except IOError, inst:
796 if inst.errno == errno.ENOENT:
796 if inst.errno == errno.ENOENT:
797 return temp
797 return temp
798 if not getattr(inst, 'filename', None):
798 if not getattr(inst, 'filename', None):
799 inst.filename = name
799 inst.filename = name
800 raise
800 raise
801 ofp = posixfile(temp, "wb")
801 ofp = posixfile(temp, "wb")
802 for chunk in filechunkiter(ifp):
802 for chunk in filechunkiter(ifp):
803 ofp.write(chunk)
803 ofp.write(chunk)
804 ifp.close()
804 ifp.close()
805 ofp.close()
805 ofp.close()
806 except:
806 except:
807 try: os.unlink(temp)
807 try: os.unlink(temp)
808 except: pass
808 except: pass
809 raise
809 raise
810 return temp
810 return temp
811
811
812 class atomictempfile(object):
812 class atomictempfile(object):
813 """file-like object that atomically updates a file
813 """file-like object that atomically updates a file
814
814
815 All writes will be redirected to a temporary copy of the original
815 All writes will be redirected to a temporary copy of the original
816 file. When rename is called, the copy is renamed to the original
816 file. When rename is called, the copy is renamed to the original
817 name, making the changes visible.
817 name, making the changes visible.
818 """
818 """
819 def __init__(self, name, mode='w+b', createmode=None):
819 def __init__(self, name, mode='w+b', createmode=None):
820 self.__name = name
820 self.__name = name
821 self._fp = None
821 self._fp = None
822 self.temp = mktempcopy(name, emptyok=('w' in mode),
822 self.temp = mktempcopy(name, emptyok=('w' in mode),
823 createmode=createmode)
823 createmode=createmode)
824 self._fp = posixfile(self.temp, mode)
824 self._fp = posixfile(self.temp, mode)
825
825
826 def __getattr__(self, name):
826 def __getattr__(self, name):
827 return getattr(self._fp, name)
827 return getattr(self._fp, name)
828
828
829 def rename(self):
829 def rename(self):
830 if not self._fp.closed:
830 if not self._fp.closed:
831 self._fp.close()
831 self._fp.close()
832 rename(self.temp, localpath(self.__name))
832 rename(self.temp, localpath(self.__name))
833
833
834 def close(self):
834 def close(self):
835 if not self._fp:
835 if not self._fp:
836 return
836 return
837 if not self._fp.closed:
837 if not self._fp.closed:
838 try:
838 try:
839 os.unlink(self.temp)
839 os.unlink(self.temp)
840 except: pass
840 except: pass
841 self._fp.close()
841 self._fp.close()
842
842
843 def __del__(self):
843 def __del__(self):
844 self.close()
844 self.close()
845
845
846 def makedirs(name, mode=None):
846 def makedirs(name, mode=None):
847 """recursive directory creation with parent mode inheritance"""
847 """recursive directory creation with parent mode inheritance"""
848 parent = os.path.abspath(os.path.dirname(name))
848 parent = os.path.abspath(os.path.dirname(name))
849 try:
849 try:
850 os.mkdir(name)
850 os.mkdir(name)
851 if mode is not None:
851 if mode is not None:
852 os.chmod(name, mode)
852 os.chmod(name, mode)
853 return
853 return
854 except OSError, err:
854 except OSError, err:
855 if err.errno == errno.EEXIST:
855 if err.errno == errno.EEXIST:
856 return
856 return
857 if not name or parent == name or err.errno != errno.ENOENT:
857 if not name or parent == name or err.errno != errno.ENOENT:
858 raise
858 raise
859 makedirs(parent, mode)
859 makedirs(parent, mode)
860 makedirs(name, mode)
860 makedirs(name, mode)
861
861
862 class opener(object):
862 class opener(object):
863 """Open files relative to a base directory
863 """Open files relative to a base directory
864
864
865 This class is used to hide the details of COW semantics and
865 This class is used to hide the details of COW semantics and
866 remote file access from higher level code.
866 remote file access from higher level code.
867 """
867 """
868 def __init__(self, base, audit=True):
868 def __init__(self, base, audit=True):
869 self.base = base
869 self.base = base
870 if audit:
870 if audit:
871 self.auditor = path_auditor(base)
871 self.auditor = path_auditor(base)
872 else:
872 else:
873 self.auditor = always
873 self.auditor = always
874 self.createmode = None
874 self.createmode = None
875 self._trustnlink = None
875 self._trustnlink = None
876
876
877 @propertycache
877 @propertycache
878 def _can_symlink(self):
878 def _can_symlink(self):
879 return checklink(self.base)
879 return checklink(self.base)
880
880
881 def _fixfilemode(self, name):
881 def _fixfilemode(self, name):
882 if self.createmode is None:
882 if self.createmode is None:
883 return
883 return
884 os.chmod(name, self.createmode & 0666)
884 os.chmod(name, self.createmode & 0666)
885
885
886 def __call__(self, path, mode="r", text=False, atomictemp=False):
886 def __call__(self, path, mode="r", text=False, atomictemp=False):
887 self.auditor(path)
887 self.auditor(path)
888 f = os.path.join(self.base, path)
888 f = os.path.join(self.base, path)
889
889
890 if not text and "b" not in mode:
890 if not text and "b" not in mode:
891 mode += "b" # for that other OS
891 mode += "b" # for that other OS
892
892
893 nlink = -1
893 nlink = -1
894 dirname, basename = os.path.split(f)
894 dirname, basename = os.path.split(f)
895 # If basename is empty, then the path is malformed because it points
895 # If basename is empty, then the path is malformed because it points
896 # to a directory. Let the posixfile() call below raise IOError.
896 # to a directory. Let the posixfile() call below raise IOError.
897 if basename and mode not in ('r', 'rb'):
897 if basename and mode not in ('r', 'rb'):
898 if atomictemp:
898 if atomictemp:
899 if not os.path.isdir(dirname):
899 if not os.path.isdir(dirname):
900 makedirs(dirname, self.createmode)
900 makedirs(dirname, self.createmode)
901 return atomictempfile(f, mode, self.createmode)
901 return atomictempfile(f, mode, self.createmode)
902 try:
902 try:
903 if 'w' in mode:
903 if 'w' in mode:
904 unlink(f)
904 unlink(f)
905 nlink = 0
905 nlink = 0
906 else:
906 else:
907 # nlinks() may behave differently for files on Windows
907 # nlinks() may behave differently for files on Windows
908 # shares if the file is open.
908 # shares if the file is open.
909 fd = posixfile(f)
909 fd = posixfile(f)
910 nlink = nlinks(f)
910 nlink = nlinks(f)
911 if nlink < 1:
911 if nlink < 1:
912 nlink = 2 # force mktempcopy (issue1922)
912 nlink = 2 # force mktempcopy (issue1922)
913 fd.close()
913 fd.close()
914 except (OSError, IOError), e:
914 except (OSError, IOError), e:
915 if e.errno != errno.ENOENT:
915 if e.errno != errno.ENOENT:
916 raise
916 raise
917 nlink = 0
917 nlink = 0
918 if not os.path.isdir(dirname):
918 if not os.path.isdir(dirname):
919 makedirs(dirname, self.createmode)
919 makedirs(dirname, self.createmode)
920 if nlink > 0:
920 if nlink > 0:
921 if self._trustnlink is None:
921 if self._trustnlink is None:
922 self._trustnlink = nlink > 1 or checknlink(f)
922 self._trustnlink = nlink > 1 or checknlink(f)
923 if nlink > 1 or not self._trustnlink:
923 if nlink > 1 or not self._trustnlink:
924 rename(mktempcopy(f), f)
924 rename(mktempcopy(f), f)
925 fp = posixfile(f, mode)
925 fp = posixfile(f, mode)
926 if nlink == 0:
926 if nlink == 0:
927 self._fixfilemode(f)
927 self._fixfilemode(f)
928 return fp
928 return fp
929
929
930 def symlink(self, src, dst):
930 def symlink(self, src, dst):
931 self.auditor(dst)
931 self.auditor(dst)
932 linkname = os.path.join(self.base, dst)
932 linkname = os.path.join(self.base, dst)
933 try:
933 try:
934 os.unlink(linkname)
934 os.unlink(linkname)
935 except OSError:
935 except OSError:
936 pass
936 pass
937
937
938 dirname = os.path.dirname(linkname)
938 dirname = os.path.dirname(linkname)
939 if not os.path.exists(dirname):
939 if not os.path.exists(dirname):
940 makedirs(dirname, self.createmode)
940 makedirs(dirname, self.createmode)
941
941
942 if self._can_symlink:
942 if self._can_symlink:
943 try:
943 try:
944 os.symlink(src, linkname)
944 os.symlink(src, linkname)
945 except OSError, err:
945 except OSError, err:
946 raise OSError(err.errno, _('could not symlink to %r: %s') %
946 raise OSError(err.errno, _('could not symlink to %r: %s') %
947 (src, err.strerror), linkname)
947 (src, err.strerror), linkname)
948 else:
948 else:
949 f = self(dst, "w")
949 f = self(dst, "w")
950 f.write(src)
950 f.write(src)
951 f.close()
951 f.close()
952 self._fixfilemode(dst)
952 self._fixfilemode(dst)
953
953
954 class chunkbuffer(object):
954 class chunkbuffer(object):
955 """Allow arbitrary sized chunks of data to be efficiently read from an
955 """Allow arbitrary sized chunks of data to be efficiently read from an
956 iterator over chunks of arbitrary size."""
956 iterator over chunks of arbitrary size."""
957
957
958 def __init__(self, in_iter):
958 def __init__(self, in_iter):
959 """in_iter is the iterator that's iterating over the input chunks.
959 """in_iter is the iterator that's iterating over the input chunks.
960 targetsize is how big a buffer to try to maintain."""
960 targetsize is how big a buffer to try to maintain."""
961 def splitbig(chunks):
961 def splitbig(chunks):
962 for chunk in chunks:
962 for chunk in chunks:
963 if len(chunk) > 2**20:
963 if len(chunk) > 2**20:
964 pos = 0
964 pos = 0
965 while pos < len(chunk):
965 while pos < len(chunk):
966 end = pos + 2 ** 18
966 end = pos + 2 ** 18
967 yield chunk[pos:end]
967 yield chunk[pos:end]
968 pos = end
968 pos = end
969 else:
969 else:
970 yield chunk
970 yield chunk
971 self.iter = splitbig(in_iter)
971 self.iter = splitbig(in_iter)
972 self._queue = []
972 self._queue = []
973
973
974 def read(self, l):
974 def read(self, l):
975 """Read L bytes of data from the iterator of chunks of data.
975 """Read L bytes of data from the iterator of chunks of data.
976 Returns less than L bytes if the iterator runs dry."""
976 Returns less than L bytes if the iterator runs dry."""
977 left = l
977 left = l
978 buf = ''
978 buf = ''
979 queue = self._queue
979 queue = self._queue
980 while left > 0:
980 while left > 0:
981 # refill the queue
981 # refill the queue
982 if not queue:
982 if not queue:
983 target = 2**18
983 target = 2**18
984 for chunk in self.iter:
984 for chunk in self.iter:
985 queue.append(chunk)
985 queue.append(chunk)
986 target -= len(chunk)
986 target -= len(chunk)
987 if target <= 0:
987 if target <= 0:
988 break
988 break
989 if not queue:
989 if not queue:
990 break
990 break
991
991
992 chunk = queue.pop(0)
992 chunk = queue.pop(0)
993 left -= len(chunk)
993 left -= len(chunk)
994 if left < 0:
994 if left < 0:
995 queue.insert(0, chunk[left:])
995 queue.insert(0, chunk[left:])
996 buf += chunk[:left]
996 buf += chunk[:left]
997 else:
997 else:
998 buf += chunk
998 buf += chunk
999
999
1000 return buf
1000 return buf
1001
1001
1002 def filechunkiter(f, size=65536, limit=None):
1002 def filechunkiter(f, size=65536, limit=None):
1003 """Create a generator that produces the data in the file size
1003 """Create a generator that produces the data in the file size
1004 (default 65536) bytes at a time, up to optional limit (default is
1004 (default 65536) bytes at a time, up to optional limit (default is
1005 to read all data). Chunks may be less than size bytes if the
1005 to read all data). Chunks may be less than size bytes if the
1006 chunk is the last chunk in the file, or the file is a socket or
1006 chunk is the last chunk in the file, or the file is a socket or
1007 some other type of file that sometimes reads less data than is
1007 some other type of file that sometimes reads less data than is
1008 requested."""
1008 requested."""
1009 assert size >= 0
1009 assert size >= 0
1010 assert limit is None or limit >= 0
1010 assert limit is None or limit >= 0
1011 while True:
1011 while True:
1012 if limit is None:
1012 if limit is None:
1013 nbytes = size
1013 nbytes = size
1014 else:
1014 else:
1015 nbytes = min(limit, size)
1015 nbytes = min(limit, size)
1016 s = nbytes and f.read(nbytes)
1016 s = nbytes and f.read(nbytes)
1017 if not s:
1017 if not s:
1018 break
1018 break
1019 if limit:
1019 if limit:
1020 limit -= len(s)
1020 limit -= len(s)
1021 yield s
1021 yield s
1022
1022
1023 def makedate():
1023 def makedate():
1024 lt = time.localtime()
1024 lt = time.localtime()
1025 if lt[8] == 1 and time.daylight:
1025 if lt[8] == 1 and time.daylight:
1026 tz = time.altzone
1026 tz = time.altzone
1027 else:
1027 else:
1028 tz = time.timezone
1028 tz = time.timezone
1029 t = time.mktime(lt)
1029 t = time.mktime(lt)
1030 if t < 0:
1030 if t < 0:
1031 hint = _("check your clock")
1031 hint = _("check your clock")
1032 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1032 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1033 return t, tz
1033 return t, tz
1034
1034
1035 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1035 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1036 """represent a (unixtime, offset) tuple as a localized time.
1036 """represent a (unixtime, offset) tuple as a localized time.
1037 unixtime is seconds since the epoch, and offset is the time zone's
1037 unixtime is seconds since the epoch, and offset is the time zone's
1038 number of seconds away from UTC. if timezone is false, do not
1038 number of seconds away from UTC. if timezone is false, do not
1039 append time zone to string."""
1039 append time zone to string."""
1040 t, tz = date or makedate()
1040 t, tz = date or makedate()
1041 if t < 0:
1041 if t < 0:
1042 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1042 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1043 tz = 0
1043 tz = 0
1044 if "%1" in format or "%2" in format:
1044 if "%1" in format or "%2" in format:
1045 sign = (tz > 0) and "-" or "+"
1045 sign = (tz > 0) and "-" or "+"
1046 minutes = abs(tz) // 60
1046 minutes = abs(tz) // 60
1047 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1047 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1048 format = format.replace("%2", "%02d" % (minutes % 60))
1048 format = format.replace("%2", "%02d" % (minutes % 60))
1049 s = time.strftime(format, time.gmtime(float(t) - tz))
1049 s = time.strftime(format, time.gmtime(float(t) - tz))
1050 return s
1050 return s
1051
1051
1052 def shortdate(date=None):
1052 def shortdate(date=None):
1053 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1053 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1054 return datestr(date, format='%Y-%m-%d')
1054 return datestr(date, format='%Y-%m-%d')
1055
1055
1056 def strdate(string, format, defaults=[]):
1056 def strdate(string, format, defaults=[]):
1057 """parse a localized time string and return a (unixtime, offset) tuple.
1057 """parse a localized time string and return a (unixtime, offset) tuple.
1058 if the string cannot be parsed, ValueError is raised."""
1058 if the string cannot be parsed, ValueError is raised."""
1059 def timezone(string):
1059 def timezone(string):
1060 tz = string.split()[-1]
1060 tz = string.split()[-1]
1061 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1061 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1062 sign = (tz[0] == "+") and 1 or -1
1062 sign = (tz[0] == "+") and 1 or -1
1063 hours = int(tz[1:3])
1063 hours = int(tz[1:3])
1064 minutes = int(tz[3:5])
1064 minutes = int(tz[3:5])
1065 return -sign * (hours * 60 + minutes) * 60
1065 return -sign * (hours * 60 + minutes) * 60
1066 if tz == "GMT" or tz == "UTC":
1066 if tz == "GMT" or tz == "UTC":
1067 return 0
1067 return 0
1068 return None
1068 return None
1069
1069
1070 # NOTE: unixtime = localunixtime + offset
1070 # NOTE: unixtime = localunixtime + offset
1071 offset, date = timezone(string), string
1071 offset, date = timezone(string), string
1072 if offset is not None:
1072 if offset is not None:
1073 date = " ".join(string.split()[:-1])
1073 date = " ".join(string.split()[:-1])
1074
1074
1075 # add missing elements from defaults
1075 # add missing elements from defaults
1076 usenow = False # default to using biased defaults
1076 usenow = False # default to using biased defaults
1077 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1077 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1078 found = [True for p in part if ("%"+p) in format]
1078 found = [True for p in part if ("%"+p) in format]
1079 if not found:
1079 if not found:
1080 date += "@" + defaults[part][usenow]
1080 date += "@" + defaults[part][usenow]
1081 format += "@%" + part[0]
1081 format += "@%" + part[0]
1082 else:
1082 else:
1083 # We've found a specific time element, less specific time
1083 # We've found a specific time element, less specific time
1084 # elements are relative to today
1084 # elements are relative to today
1085 usenow = True
1085 usenow = True
1086
1086
1087 timetuple = time.strptime(date, format)
1087 timetuple = time.strptime(date, format)
1088 localunixtime = int(calendar.timegm(timetuple))
1088 localunixtime = int(calendar.timegm(timetuple))
1089 if offset is None:
1089 if offset is None:
1090 # local timezone
1090 # local timezone
1091 unixtime = int(time.mktime(timetuple))
1091 unixtime = int(time.mktime(timetuple))
1092 offset = unixtime - localunixtime
1092 offset = unixtime - localunixtime
1093 else:
1093 else:
1094 unixtime = localunixtime + offset
1094 unixtime = localunixtime + offset
1095 return unixtime, offset
1095 return unixtime, offset
1096
1096
1097 def parsedate(date, formats=None, bias={}):
1097 def parsedate(date, formats=None, bias={}):
1098 """parse a localized date/time and return a (unixtime, offset) tuple.
1098 """parse a localized date/time and return a (unixtime, offset) tuple.
1099
1099
1100 The date may be a "unixtime offset" string or in one of the specified
1100 The date may be a "unixtime offset" string or in one of the specified
1101 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1101 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1102 """
1102 """
1103 if not date:
1103 if not date:
1104 return 0, 0
1104 return 0, 0
1105 if isinstance(date, tuple) and len(date) == 2:
1105 if isinstance(date, tuple) and len(date) == 2:
1106 return date
1106 return date
1107 if not formats:
1107 if not formats:
1108 formats = defaultdateformats
1108 formats = defaultdateformats
1109 date = date.strip()
1109 date = date.strip()
1110 try:
1110 try:
1111 when, offset = map(int, date.split(' '))
1111 when, offset = map(int, date.split(' '))
1112 except ValueError:
1112 except ValueError:
1113 # fill out defaults
1113 # fill out defaults
1114 now = makedate()
1114 now = makedate()
1115 defaults = {}
1115 defaults = {}
1116 nowmap = {}
1116 nowmap = {}
1117 for part in ("d", "mb", "yY", "HI", "M", "S"):
1117 for part in ("d", "mb", "yY", "HI", "M", "S"):
1118 # this piece is for rounding the specific end of unknowns
1118 # this piece is for rounding the specific end of unknowns
1119 b = bias.get(part)
1119 b = bias.get(part)
1120 if b is None:
1120 if b is None:
1121 if part[0] in "HMS":
1121 if part[0] in "HMS":
1122 b = "00"
1122 b = "00"
1123 else:
1123 else:
1124 b = "0"
1124 b = "0"
1125
1125
1126 # this piece is for matching the generic end to today's date
1126 # this piece is for matching the generic end to today's date
1127 n = datestr(now, "%" + part[0])
1127 n = datestr(now, "%" + part[0])
1128
1128
1129 defaults[part] = (b, n)
1129 defaults[part] = (b, n)
1130
1130
1131 for format in formats:
1131 for format in formats:
1132 try:
1132 try:
1133 when, offset = strdate(date, format, defaults)
1133 when, offset = strdate(date, format, defaults)
1134 except (ValueError, OverflowError):
1134 except (ValueError, OverflowError):
1135 pass
1135 pass
1136 else:
1136 else:
1137 break
1137 break
1138 else:
1138 else:
1139 raise Abort(_('invalid date: %r') % date)
1139 raise Abort(_('invalid date: %r') % date)
1140 # validate explicit (probably user-specified) date and
1140 # validate explicit (probably user-specified) date and
1141 # time zone offset. values must fit in signed 32 bits for
1141 # time zone offset. values must fit in signed 32 bits for
1142 # current 32-bit linux runtimes. timezones go from UTC-12
1142 # current 32-bit linux runtimes. timezones go from UTC-12
1143 # to UTC+14
1143 # to UTC+14
1144 if abs(when) > 0x7fffffff:
1144 if abs(when) > 0x7fffffff:
1145 raise Abort(_('date exceeds 32 bits: %d') % when)
1145 raise Abort(_('date exceeds 32 bits: %d') % when)
1146 if when < 0:
1146 if when < 0:
1147 raise Abort(_('negative date value: %d') % when)
1147 raise Abort(_('negative date value: %d') % when)
1148 if offset < -50400 or offset > 43200:
1148 if offset < -50400 or offset > 43200:
1149 raise Abort(_('impossible time zone offset: %d') % offset)
1149 raise Abort(_('impossible time zone offset: %d') % offset)
1150 return when, offset
1150 return when, offset
1151
1151
1152 def matchdate(date):
1152 def matchdate(date):
1153 """Return a function that matches a given date match specifier
1153 """Return a function that matches a given date match specifier
1154
1154
1155 Formats include:
1155 Formats include:
1156
1156
1157 '{date}' match a given date to the accuracy provided
1157 '{date}' match a given date to the accuracy provided
1158
1158
1159 '<{date}' on or before a given date
1159 '<{date}' on or before a given date
1160
1160
1161 '>{date}' on or after a given date
1161 '>{date}' on or after a given date
1162
1162
1163 >>> p1 = parsedate("10:29:59")
1163 >>> p1 = parsedate("10:29:59")
1164 >>> p2 = parsedate("10:30:00")
1164 >>> p2 = parsedate("10:30:00")
1165 >>> p3 = parsedate("10:30:59")
1165 >>> p3 = parsedate("10:30:59")
1166 >>> p4 = parsedate("10:31:00")
1166 >>> p4 = parsedate("10:31:00")
1167 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1167 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1168 >>> f = matchdate("10:30")
1168 >>> f = matchdate("10:30")
1169 >>> f(p1[0])
1169 >>> f(p1[0])
1170 False
1170 False
1171 >>> f(p2[0])
1171 >>> f(p2[0])
1172 True
1172 True
1173 >>> f(p3[0])
1173 >>> f(p3[0])
1174 True
1174 True
1175 >>> f(p4[0])
1175 >>> f(p4[0])
1176 False
1176 False
1177 >>> f(p5[0])
1177 >>> f(p5[0])
1178 False
1178 False
1179 """
1179 """
1180
1180
1181 def lower(date):
1181 def lower(date):
1182 d = dict(mb="1", d="1")
1182 d = dict(mb="1", d="1")
1183 return parsedate(date, extendeddateformats, d)[0]
1183 return parsedate(date, extendeddateformats, d)[0]
1184
1184
1185 def upper(date):
1185 def upper(date):
1186 d = dict(mb="12", HI="23", M="59", S="59")
1186 d = dict(mb="12", HI="23", M="59", S="59")
1187 for days in ("31", "30", "29"):
1187 for days in ("31", "30", "29"):
1188 try:
1188 try:
1189 d["d"] = days
1189 d["d"] = days
1190 return parsedate(date, extendeddateformats, d)[0]
1190 return parsedate(date, extendeddateformats, d)[0]
1191 except:
1191 except:
1192 pass
1192 pass
1193 d["d"] = "28"
1193 d["d"] = "28"
1194 return parsedate(date, extendeddateformats, d)[0]
1194 return parsedate(date, extendeddateformats, d)[0]
1195
1195
1196 date = date.strip()
1196 date = date.strip()
1197 if date[0] == "<":
1197 if date[0] == "<":
1198 when = upper(date[1:])
1198 when = upper(date[1:])
1199 return lambda x: x <= when
1199 return lambda x: x <= when
1200 elif date[0] == ">":
1200 elif date[0] == ">":
1201 when = lower(date[1:])
1201 when = lower(date[1:])
1202 return lambda x: x >= when
1202 return lambda x: x >= when
1203 elif date[0] == "-":
1203 elif date[0] == "-":
1204 try:
1204 try:
1205 days = int(date[1:])
1205 days = int(date[1:])
1206 except ValueError:
1206 except ValueError:
1207 raise Abort(_("invalid day spec: %s") % date[1:])
1207 raise Abort(_("invalid day spec: %s") % date[1:])
1208 when = makedate()[0] - days * 3600 * 24
1208 when = makedate()[0] - days * 3600 * 24
1209 return lambda x: x >= when
1209 return lambda x: x >= when
1210 elif " to " in date:
1210 elif " to " in date:
1211 a, b = date.split(" to ")
1211 a, b = date.split(" to ")
1212 start, stop = lower(a), upper(b)
1212 start, stop = lower(a), upper(b)
1213 return lambda x: x >= start and x <= stop
1213 return lambda x: x >= start and x <= stop
1214 else:
1214 else:
1215 start, stop = lower(date), upper(date)
1215 start, stop = lower(date), upper(date)
1216 return lambda x: x >= start and x <= stop
1216 return lambda x: x >= start and x <= stop
1217
1217
1218 def shortuser(user):
1218 def shortuser(user):
1219 """Return a short representation of a user name or email address."""
1219 """Return a short representation of a user name or email address."""
1220 f = user.find('@')
1220 f = user.find('@')
1221 if f >= 0:
1221 if f >= 0:
1222 user = user[:f]
1222 user = user[:f]
1223 f = user.find('<')
1223 f = user.find('<')
1224 if f >= 0:
1224 if f >= 0:
1225 user = user[f + 1:]
1225 user = user[f + 1:]
1226 f = user.find(' ')
1226 f = user.find(' ')
1227 if f >= 0:
1227 if f >= 0:
1228 user = user[:f]
1228 user = user[:f]
1229 f = user.find('.')
1229 f = user.find('.')
1230 if f >= 0:
1230 if f >= 0:
1231 user = user[:f]
1231 user = user[:f]
1232 return user
1232 return user
1233
1233
1234 def email(author):
1234 def email(author):
1235 '''get email of author.'''
1235 '''get email of author.'''
1236 r = author.find('>')
1236 r = author.find('>')
1237 if r == -1:
1237 if r == -1:
1238 r = None
1238 r = None
1239 return author[author.find('<') + 1:r]
1239 return author[author.find('<') + 1:r]
1240
1240
1241 def _ellipsis(text, maxlength):
1241 def _ellipsis(text, maxlength):
1242 if len(text) <= maxlength:
1242 if len(text) <= maxlength:
1243 return text, False
1243 return text, False
1244 else:
1244 else:
1245 return "%s..." % (text[:maxlength - 3]), True
1245 return "%s..." % (text[:maxlength - 3]), True
1246
1246
1247 def ellipsis(text, maxlength=400):
1247 def ellipsis(text, maxlength=400):
1248 """Trim string to at most maxlength (default: 400) characters."""
1248 """Trim string to at most maxlength (default: 400) characters."""
1249 try:
1249 try:
1250 # use unicode not to split at intermediate multi-byte sequence
1250 # use unicode not to split at intermediate multi-byte sequence
1251 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1251 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1252 maxlength)
1252 maxlength)
1253 if not truncated:
1253 if not truncated:
1254 return text
1254 return text
1255 return utext.encode(encoding.encoding)
1255 return utext.encode(encoding.encoding)
1256 except (UnicodeDecodeError, UnicodeEncodeError):
1256 except (UnicodeDecodeError, UnicodeEncodeError):
1257 return _ellipsis(text, maxlength)[0]
1257 return _ellipsis(text, maxlength)[0]
1258
1258
1259 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1259 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1260 '''yield every hg repository under path, recursively.'''
1260 '''yield every hg repository under path, recursively.'''
1261 def errhandler(err):
1261 def errhandler(err):
1262 if err.filename == path:
1262 if err.filename == path:
1263 raise err
1263 raise err
1264 if followsym and hasattr(os.path, 'samestat'):
1264 if followsym and hasattr(os.path, 'samestat'):
1265 def _add_dir_if_not_there(dirlst, dirname):
1265 def _add_dir_if_not_there(dirlst, dirname):
1266 match = False
1266 match = False
1267 samestat = os.path.samestat
1267 samestat = os.path.samestat
1268 dirstat = os.stat(dirname)
1268 dirstat = os.stat(dirname)
1269 for lstdirstat in dirlst:
1269 for lstdirstat in dirlst:
1270 if samestat(dirstat, lstdirstat):
1270 if samestat(dirstat, lstdirstat):
1271 match = True
1271 match = True
1272 break
1272 break
1273 if not match:
1273 if not match:
1274 dirlst.append(dirstat)
1274 dirlst.append(dirstat)
1275 return not match
1275 return not match
1276 else:
1276 else:
1277 followsym = False
1277 followsym = False
1278
1278
1279 if (seen_dirs is None) and followsym:
1279 if (seen_dirs is None) and followsym:
1280 seen_dirs = []
1280 seen_dirs = []
1281 _add_dir_if_not_there(seen_dirs, path)
1281 _add_dir_if_not_there(seen_dirs, path)
1282 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1282 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1283 dirs.sort()
1283 dirs.sort()
1284 if '.hg' in dirs:
1284 if '.hg' in dirs:
1285 yield root # found a repository
1285 yield root # found a repository
1286 qroot = os.path.join(root, '.hg', 'patches')
1286 qroot = os.path.join(root, '.hg', 'patches')
1287 if os.path.isdir(os.path.join(qroot, '.hg')):
1287 if os.path.isdir(os.path.join(qroot, '.hg')):
1288 yield qroot # we have a patch queue repo here
1288 yield qroot # we have a patch queue repo here
1289 if recurse:
1289 if recurse:
1290 # avoid recursing inside the .hg directory
1290 # avoid recursing inside the .hg directory
1291 dirs.remove('.hg')
1291 dirs.remove('.hg')
1292 else:
1292 else:
1293 dirs[:] = [] # don't descend further
1293 dirs[:] = [] # don't descend further
1294 elif followsym:
1294 elif followsym:
1295 newdirs = []
1295 newdirs = []
1296 for d in dirs:
1296 for d in dirs:
1297 fname = os.path.join(root, d)
1297 fname = os.path.join(root, d)
1298 if _add_dir_if_not_there(seen_dirs, fname):
1298 if _add_dir_if_not_there(seen_dirs, fname):
1299 if os.path.islink(fname):
1299 if os.path.islink(fname):
1300 for hgname in walkrepos(fname, True, seen_dirs):
1300 for hgname in walkrepos(fname, True, seen_dirs):
1301 yield hgname
1301 yield hgname
1302 else:
1302 else:
1303 newdirs.append(d)
1303 newdirs.append(d)
1304 dirs[:] = newdirs
1304 dirs[:] = newdirs
1305
1305
1306 _rcpath = None
1306 _rcpath = None
1307
1307
1308 def os_rcpath():
1308 def os_rcpath():
1309 '''return default os-specific hgrc search path'''
1309 '''return default os-specific hgrc search path'''
1310 path = system_rcpath()
1310 path = system_rcpath()
1311 path.extend(user_rcpath())
1311 path.extend(user_rcpath())
1312 path = [os.path.normpath(f) for f in path]
1312 path = [os.path.normpath(f) for f in path]
1313 return path
1313 return path
1314
1314
1315 def rcpath():
1315 def rcpath():
1316 '''return hgrc search path. if env var HGRCPATH is set, use it.
1316 '''return hgrc search path. if env var HGRCPATH is set, use it.
1317 for each item in path, if directory, use files ending in .rc,
1317 for each item in path, if directory, use files ending in .rc,
1318 else use item.
1318 else use item.
1319 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1319 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1320 if no HGRCPATH, use default os-specific path.'''
1320 if no HGRCPATH, use default os-specific path.'''
1321 global _rcpath
1321 global _rcpath
1322 if _rcpath is None:
1322 if _rcpath is None:
1323 if 'HGRCPATH' in os.environ:
1323 if 'HGRCPATH' in os.environ:
1324 _rcpath = []
1324 _rcpath = []
1325 for p in os.environ['HGRCPATH'].split(os.pathsep):
1325 for p in os.environ['HGRCPATH'].split(os.pathsep):
1326 if not p:
1326 if not p:
1327 continue
1327 continue
1328 p = expandpath(p)
1328 p = expandpath(p)
1329 if os.path.isdir(p):
1329 if os.path.isdir(p):
1330 for f, kind in osutil.listdir(p):
1330 for f, kind in osutil.listdir(p):
1331 if f.endswith('.rc'):
1331 if f.endswith('.rc'):
1332 _rcpath.append(os.path.join(p, f))
1332 _rcpath.append(os.path.join(p, f))
1333 else:
1333 else:
1334 _rcpath.append(p)
1334 _rcpath.append(p)
1335 else:
1335 else:
1336 _rcpath = os_rcpath()
1336 _rcpath = os_rcpath()
1337 return _rcpath
1337 return _rcpath
1338
1338
1339 def bytecount(nbytes):
1339 def bytecount(nbytes):
1340 '''return byte count formatted as readable string, with units'''
1340 '''return byte count formatted as readable string, with units'''
1341
1341
1342 units = (
1342 units = (
1343 (100, 1 << 30, _('%.0f GB')),
1343 (100, 1 << 30, _('%.0f GB')),
1344 (10, 1 << 30, _('%.1f GB')),
1344 (10, 1 << 30, _('%.1f GB')),
1345 (1, 1 << 30, _('%.2f GB')),
1345 (1, 1 << 30, _('%.2f GB')),
1346 (100, 1 << 20, _('%.0f MB')),
1346 (100, 1 << 20, _('%.0f MB')),
1347 (10, 1 << 20, _('%.1f MB')),
1347 (10, 1 << 20, _('%.1f MB')),
1348 (1, 1 << 20, _('%.2f MB')),
1348 (1, 1 << 20, _('%.2f MB')),
1349 (100, 1 << 10, _('%.0f KB')),
1349 (100, 1 << 10, _('%.0f KB')),
1350 (10, 1 << 10, _('%.1f KB')),
1350 (10, 1 << 10, _('%.1f KB')),
1351 (1, 1 << 10, _('%.2f KB')),
1351 (1, 1 << 10, _('%.2f KB')),
1352 (1, 1, _('%.0f bytes')),
1352 (1, 1, _('%.0f bytes')),
1353 )
1353 )
1354
1354
1355 for multiplier, divisor, format in units:
1355 for multiplier, divisor, format in units:
1356 if nbytes >= divisor * multiplier:
1356 if nbytes >= divisor * multiplier:
1357 return format % (nbytes / float(divisor))
1357 return format % (nbytes / float(divisor))
1358 return units[-1][2] % nbytes
1358 return units[-1][2] % nbytes
1359
1359
1360 def drop_scheme(scheme, path):
1360 def drop_scheme(scheme, path):
1361 sc = scheme + ':'
1361 sc = scheme + ':'
1362 if path.startswith(sc):
1362 if path.startswith(sc):
1363 path = path[len(sc):]
1363 path = path[len(sc):]
1364 if path.startswith('//'):
1364 if path.startswith('//'):
1365 if scheme == 'file':
1365 if scheme == 'file':
1366 i = path.find('/', 2)
1366 i = path.find('/', 2)
1367 if i == -1:
1367 if i == -1:
1368 return ''
1368 return ''
1369 # On Windows, absolute paths are rooted at the current drive
1369 # On Windows, absolute paths are rooted at the current drive
1370 # root. On POSIX they are rooted at the file system root.
1370 # root. On POSIX they are rooted at the file system root.
1371 if os.name == 'nt':
1371 if os.name == 'nt':
1372 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1372 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1373 path = os.path.join(droot, path[i + 1:])
1373 path = os.path.join(droot, path[i + 1:])
1374 else:
1374 else:
1375 path = path[i:]
1375 path = path[i:]
1376 else:
1376 else:
1377 path = path[2:]
1377 path = path[2:]
1378 return path
1378 return path
1379
1379
1380 def uirepr(s):
1380 def uirepr(s):
1381 # Avoid double backslash in Windows path repr()
1381 # Avoid double backslash in Windows path repr()
1382 return repr(s).replace('\\\\', '\\')
1382 return repr(s).replace('\\\\', '\\')
1383
1383
1384 # delay import of textwrap
1384 # delay import of textwrap
1385 def MBTextWrapper(**kwargs):
1385 def MBTextWrapper(**kwargs):
1386 class tw(textwrap.TextWrapper):
1386 class tw(textwrap.TextWrapper):
1387 """
1387 """
1388 Extend TextWrapper for double-width characters.
1388 Extend TextWrapper for double-width characters.
1389
1389
1390 Some Asian characters use two terminal columns instead of one.
1390 Some Asian characters use two terminal columns instead of one.
1391 A good example of this behavior can be seen with u'\u65e5\u672c',
1391 A good example of this behavior can be seen with u'\u65e5\u672c',
1392 the two Japanese characters for "Japan":
1392 the two Japanese characters for "Japan":
1393 len() returns 2, but when printed to a terminal, they eat 4 columns.
1393 len() returns 2, but when printed to a terminal, they eat 4 columns.
1394
1394
1395 (Note that this has nothing to do whatsoever with unicode
1395 (Note that this has nothing to do whatsoever with unicode
1396 representation, or encoding of the underlying string)
1396 representation, or encoding of the underlying string)
1397 """
1397 """
1398 def __init__(self, **kwargs):
1398 def __init__(self, **kwargs):
1399 textwrap.TextWrapper.__init__(self, **kwargs)
1399 textwrap.TextWrapper.__init__(self, **kwargs)
1400
1400
1401 def _cutdown(self, str, space_left):
1401 def _cutdown(self, str, space_left):
1402 l = 0
1402 l = 0
1403 ucstr = unicode(str, encoding.encoding)
1403 ucstr = unicode(str, encoding.encoding)
1404 colwidth = unicodedata.east_asian_width
1404 colwidth = unicodedata.east_asian_width
1405 for i in xrange(len(ucstr)):
1405 for i in xrange(len(ucstr)):
1406 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1406 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1407 if space_left < l:
1407 if space_left < l:
1408 return (ucstr[:i].encode(encoding.encoding),
1408 return (ucstr[:i].encode(encoding.encoding),
1409 ucstr[i:].encode(encoding.encoding))
1409 ucstr[i:].encode(encoding.encoding))
1410 return str, ''
1410 return str, ''
1411
1411
1412 # overriding of base class
1412 # overriding of base class
1413 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1413 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1414 space_left = max(width - cur_len, 1)
1414 space_left = max(width - cur_len, 1)
1415
1415
1416 if self.break_long_words:
1416 if self.break_long_words:
1417 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1417 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1418 cur_line.append(cut)
1418 cur_line.append(cut)
1419 reversed_chunks[-1] = res
1419 reversed_chunks[-1] = res
1420 elif not cur_line:
1420 elif not cur_line:
1421 cur_line.append(reversed_chunks.pop())
1421 cur_line.append(reversed_chunks.pop())
1422
1422
1423 global MBTextWrapper
1423 global MBTextWrapper
1424 MBTextWrapper = tw
1424 MBTextWrapper = tw
1425 return tw(**kwargs)
1425 return tw(**kwargs)
1426
1426
1427 def wrap(line, width, initindent='', hangindent=''):
1427 def wrap(line, width, initindent='', hangindent=''):
1428 maxindent = max(len(hangindent), len(initindent))
1428 maxindent = max(len(hangindent), len(initindent))
1429 if width <= maxindent:
1429 if width <= maxindent:
1430 # adjust for weird terminal size
1430 # adjust for weird terminal size
1431 width = max(78, maxindent + 1)
1431 width = max(78, maxindent + 1)
1432 wrapper = MBTextWrapper(width=width,
1432 wrapper = MBTextWrapper(width=width,
1433 initial_indent=initindent,
1433 initial_indent=initindent,
1434 subsequent_indent=hangindent)
1434 subsequent_indent=hangindent)
1435 return wrapper.fill(line)
1435 return wrapper.fill(line)
1436
1436
1437 def iterlines(iterator):
1437 def iterlines(iterator):
1438 for chunk in iterator:
1438 for chunk in iterator:
1439 for line in chunk.splitlines():
1439 for line in chunk.splitlines():
1440 yield line
1440 yield line
1441
1441
1442 def expandpath(path):
1442 def expandpath(path):
1443 return os.path.expanduser(os.path.expandvars(path))
1443 return os.path.expanduser(os.path.expandvars(path))
1444
1444
1445 def hgcmd():
1445 def hgcmd():
1446 """Return the command used to execute current hg
1446 """Return the command used to execute current hg
1447
1447
1448 This is different from hgexecutable() because on Windows we want
1448 This is different from hgexecutable() because on Windows we want
1449 to avoid things opening new shell windows like batch files, so we
1449 to avoid things opening new shell windows like batch files, so we
1450 get either the python call or current executable.
1450 get either the python call or current executable.
1451 """
1451 """
1452 if main_is_frozen():
1452 if main_is_frozen():
1453 return [sys.executable]
1453 return [sys.executable]
1454 return gethgcmd()
1454 return gethgcmd()
1455
1455
1456 def rundetached(args, condfn):
1456 def rundetached(args, condfn):
1457 """Execute the argument list in a detached process.
1457 """Execute the argument list in a detached process.
1458
1458
1459 condfn is a callable which is called repeatedly and should return
1459 condfn is a callable which is called repeatedly and should return
1460 True once the child process is known to have started successfully.
1460 True once the child process is known to have started successfully.
1461 At this point, the child process PID is returned. If the child
1461 At this point, the child process PID is returned. If the child
1462 process fails to start or finishes before condfn() evaluates to
1462 process fails to start or finishes before condfn() evaluates to
1463 True, return -1.
1463 True, return -1.
1464 """
1464 """
1465 # Windows case is easier because the child process is either
1465 # Windows case is easier because the child process is either
1466 # successfully starting and validating the condition or exiting
1466 # successfully starting and validating the condition or exiting
1467 # on failure. We just poll on its PID. On Unix, if the child
1467 # on failure. We just poll on its PID. On Unix, if the child
1468 # process fails to start, it will be left in a zombie state until
1468 # process fails to start, it will be left in a zombie state until
1469 # the parent wait on it, which we cannot do since we expect a long
1469 # the parent wait on it, which we cannot do since we expect a long
1470 # running process on success. Instead we listen for SIGCHLD telling
1470 # running process on success. Instead we listen for SIGCHLD telling
1471 # us our child process terminated.
1471 # us our child process terminated.
1472 terminated = set()
1472 terminated = set()
1473 def handler(signum, frame):
1473 def handler(signum, frame):
1474 terminated.add(os.wait())
1474 terminated.add(os.wait())
1475 prevhandler = None
1475 prevhandler = None
1476 if hasattr(signal, 'SIGCHLD'):
1476 if hasattr(signal, 'SIGCHLD'):
1477 prevhandler = signal.signal(signal.SIGCHLD, handler)
1477 prevhandler = signal.signal(signal.SIGCHLD, handler)
1478 try:
1478 try:
1479 pid = spawndetached(args)
1479 pid = spawndetached(args)
1480 while not condfn():
1480 while not condfn():
1481 if ((pid in terminated or not testpid(pid))
1481 if ((pid in terminated or not testpid(pid))
1482 and not condfn()):
1482 and not condfn()):
1483 return -1
1483 return -1
1484 time.sleep(0.1)
1484 time.sleep(0.1)
1485 return pid
1485 return pid
1486 finally:
1486 finally:
1487 if prevhandler is not None:
1487 if prevhandler is not None:
1488 signal.signal(signal.SIGCHLD, prevhandler)
1488 signal.signal(signal.SIGCHLD, prevhandler)
1489
1489
1490 try:
1490 try:
1491 any, all = any, all
1491 any, all = any, all
1492 except NameError:
1492 except NameError:
1493 def any(iterable):
1493 def any(iterable):
1494 for i in iterable:
1494 for i in iterable:
1495 if i:
1495 if i:
1496 return True
1496 return True
1497 return False
1497 return False
1498
1498
1499 def all(iterable):
1499 def all(iterable):
1500 for i in iterable:
1500 for i in iterable:
1501 if not i:
1501 if not i:
1502 return False
1502 return False
1503 return True
1503 return True
1504
1504
1505 def interpolate(prefix, mapping, s, fn=None):
1505 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1506 """Return the result of interpolating items in the mapping into string s.
1506 """Return the result of interpolating items in the mapping into string s.
1507
1507
1508 prefix is a single character string, or a two character string with
1508 prefix is a single character string, or a two character string with
1509 a backslash as the first character if the prefix needs to be escaped in
1509 a backslash as the first character if the prefix needs to be escaped in
1510 a regular expression.
1510 a regular expression.
1511
1511
1512 fn is an optional function that will be applied to the replacement text
1512 fn is an optional function that will be applied to the replacement text
1513 just before replacement.
1513 just before replacement.
1514
1515 escape_prefix is an optional flag that allows using doubled prefix for
1516 its escaping.
1514 """
1517 """
1515 fn = fn or (lambda s: s)
1518 fn = fn or (lambda s: s)
1516 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1519 patterns = '|'.join(mapping.keys())
1520 if escape_prefix:
1521 patterns += '|' + prefix
1522 if len(prefix) > 1:
1523 prefix_char = prefix[1:]
1524 else:
1525 prefix_char = prefix
1526 mapping[prefix_char] = prefix_char
1527 r = re.compile(r'%s(%s)' % (prefix, patterns))
1517 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1528 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1518
1529
1519 def getport(port):
1530 def getport(port):
1520 """Return the port for a given network service.
1531 """Return the port for a given network service.
1521
1532
1522 If port is an integer, it's returned as is. If it's a string, it's
1533 If port is an integer, it's returned as is. If it's a string, it's
1523 looked up using socket.getservbyname(). If there's no matching
1534 looked up using socket.getservbyname(). If there's no matching
1524 service, util.Abort is raised.
1535 service, util.Abort is raised.
1525 """
1536 """
1526 try:
1537 try:
1527 return int(port)
1538 return int(port)
1528 except ValueError:
1539 except ValueError:
1529 pass
1540 pass
1530
1541
1531 try:
1542 try:
1532 return socket.getservbyname(port)
1543 return socket.getservbyname(port)
1533 except socket.error:
1544 except socket.error:
1534 raise Abort(_("no port number associated with service '%s'") % port)
1545 raise Abort(_("no port number associated with service '%s'") % port)
1535
1546
1536 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1547 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1537 '0': False, 'no': False, 'false': False, 'off': False,
1548 '0': False, 'no': False, 'false': False, 'off': False,
1538 'never': False}
1549 'never': False}
1539
1550
1540 def parsebool(s):
1551 def parsebool(s):
1541 """Parse s into a boolean.
1552 """Parse s into a boolean.
1542
1553
1543 If s is not a valid boolean, returns None.
1554 If s is not a valid boolean, returns None.
1544 """
1555 """
1545 return _booleans.get(s.lower(), None)
1556 return _booleans.get(s.lower(), None)
General Comments 0
You need to be logged in to leave comments. Login now