##// END OF EJS Templates
dispatch: generalize signature checking for extension command wrapping
Matt Mackall -
r7388:57516312 default
parent child Browse files
Show More
@@ -1,420 +1,416
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 from repo import RepoError
9 from repo import RepoError
10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex, time
10 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
11 import util, commands, hg, lock, fancyopts, revlog, version, extensions, hook
11 import util, commands, hg, lock, fancyopts, revlog, version, extensions, hook
12 import cmdutil
12 import cmdutil
13 import ui as _ui
13 import ui as _ui
14
14
15 class ParseError(Exception):
15 class ParseError(Exception):
16 """Exception raised on errors in parsing the command line."""
16 """Exception raised on errors in parsing the command line."""
17
17
18 def run():
18 def run():
19 "run the command in sys.argv"
19 "run the command in sys.argv"
20 sys.exit(dispatch(sys.argv[1:]))
20 sys.exit(dispatch(sys.argv[1:]))
21
21
22 def dispatch(args):
22 def dispatch(args):
23 "run the command specified in args"
23 "run the command specified in args"
24 try:
24 try:
25 u = _ui.ui(traceback='--traceback' in args)
25 u = _ui.ui(traceback='--traceback' in args)
26 except util.Abort, inst:
26 except util.Abort, inst:
27 sys.stderr.write(_("abort: %s\n") % inst)
27 sys.stderr.write(_("abort: %s\n") % inst)
28 return -1
28 return -1
29 return _runcatch(u, args)
29 return _runcatch(u, args)
30
30
31 def _runcatch(ui, args):
31 def _runcatch(ui, args):
32 def catchterm(*args):
32 def catchterm(*args):
33 raise util.SignalInterrupt
33 raise util.SignalInterrupt
34
34
35 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
35 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
36 num = getattr(signal, name, None)
36 num = getattr(signal, name, None)
37 if num: signal.signal(num, catchterm)
37 if num: signal.signal(num, catchterm)
38
38
39 try:
39 try:
40 try:
40 try:
41 # enter the debugger before command execution
41 # enter the debugger before command execution
42 if '--debugger' in args:
42 if '--debugger' in args:
43 pdb.set_trace()
43 pdb.set_trace()
44 try:
44 try:
45 return _dispatch(ui, args)
45 return _dispatch(ui, args)
46 finally:
46 finally:
47 ui.flush()
47 ui.flush()
48 except:
48 except:
49 # enter the debugger when we hit an exception
49 # enter the debugger when we hit an exception
50 if '--debugger' in args:
50 if '--debugger' in args:
51 pdb.post_mortem(sys.exc_info()[2])
51 pdb.post_mortem(sys.exc_info()[2])
52 ui.print_exc()
52 ui.print_exc()
53 raise
53 raise
54
54
55 except ParseError, inst:
55 except ParseError, inst:
56 if inst.args[0]:
56 if inst.args[0]:
57 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
57 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
58 commands.help_(ui, inst.args[0])
58 commands.help_(ui, inst.args[0])
59 else:
59 else:
60 ui.warn(_("hg: %s\n") % inst.args[1])
60 ui.warn(_("hg: %s\n") % inst.args[1])
61 commands.help_(ui, 'shortlist')
61 commands.help_(ui, 'shortlist')
62 except cmdutil.AmbiguousCommand, inst:
62 except cmdutil.AmbiguousCommand, inst:
63 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
63 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
64 (inst.args[0], " ".join(inst.args[1])))
64 (inst.args[0], " ".join(inst.args[1])))
65 except cmdutil.UnknownCommand, inst:
65 except cmdutil.UnknownCommand, inst:
66 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
66 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
67 commands.help_(ui, 'shortlist')
67 commands.help_(ui, 'shortlist')
68 except RepoError, inst:
68 except RepoError, inst:
69 ui.warn(_("abort: %s!\n") % inst)
69 ui.warn(_("abort: %s!\n") % inst)
70 except lock.LockHeld, inst:
70 except lock.LockHeld, inst:
71 if inst.errno == errno.ETIMEDOUT:
71 if inst.errno == errno.ETIMEDOUT:
72 reason = _('timed out waiting for lock held by %s') % inst.locker
72 reason = _('timed out waiting for lock held by %s') % inst.locker
73 else:
73 else:
74 reason = _('lock held by %s') % inst.locker
74 reason = _('lock held by %s') % inst.locker
75 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
75 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
76 except lock.LockUnavailable, inst:
76 except lock.LockUnavailable, inst:
77 ui.warn(_("abort: could not lock %s: %s\n") %
77 ui.warn(_("abort: could not lock %s: %s\n") %
78 (inst.desc or inst.filename, inst.strerror))
78 (inst.desc or inst.filename, inst.strerror))
79 except revlog.RevlogError, inst:
79 except revlog.RevlogError, inst:
80 ui.warn(_("abort: %s!\n") % inst)
80 ui.warn(_("abort: %s!\n") % inst)
81 except util.SignalInterrupt:
81 except util.SignalInterrupt:
82 ui.warn(_("killed!\n"))
82 ui.warn(_("killed!\n"))
83 except KeyboardInterrupt:
83 except KeyboardInterrupt:
84 try:
84 try:
85 ui.warn(_("interrupted!\n"))
85 ui.warn(_("interrupted!\n"))
86 except IOError, inst:
86 except IOError, inst:
87 if inst.errno == errno.EPIPE:
87 if inst.errno == errno.EPIPE:
88 if ui.debugflag:
88 if ui.debugflag:
89 ui.warn(_("\nbroken pipe\n"))
89 ui.warn(_("\nbroken pipe\n"))
90 else:
90 else:
91 raise
91 raise
92 except socket.error, inst:
92 except socket.error, inst:
93 ui.warn(_("abort: %s\n") % inst[-1])
93 ui.warn(_("abort: %s\n") % inst[-1])
94 except IOError, inst:
94 except IOError, inst:
95 if hasattr(inst, "code"):
95 if hasattr(inst, "code"):
96 ui.warn(_("abort: %s\n") % inst)
96 ui.warn(_("abort: %s\n") % inst)
97 elif hasattr(inst, "reason"):
97 elif hasattr(inst, "reason"):
98 try: # usually it is in the form (errno, strerror)
98 try: # usually it is in the form (errno, strerror)
99 reason = inst.reason.args[1]
99 reason = inst.reason.args[1]
100 except: # it might be anything, for example a string
100 except: # it might be anything, for example a string
101 reason = inst.reason
101 reason = inst.reason
102 ui.warn(_("abort: error: %s\n") % reason)
102 ui.warn(_("abort: error: %s\n") % reason)
103 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
103 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
104 if ui.debugflag:
104 if ui.debugflag:
105 ui.warn(_("broken pipe\n"))
105 ui.warn(_("broken pipe\n"))
106 elif getattr(inst, "strerror", None):
106 elif getattr(inst, "strerror", None):
107 if getattr(inst, "filename", None):
107 if getattr(inst, "filename", None):
108 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
108 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
109 else:
109 else:
110 ui.warn(_("abort: %s\n") % inst.strerror)
110 ui.warn(_("abort: %s\n") % inst.strerror)
111 else:
111 else:
112 raise
112 raise
113 except OSError, inst:
113 except OSError, inst:
114 if getattr(inst, "filename", None):
114 if getattr(inst, "filename", None):
115 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
115 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
116 else:
116 else:
117 ui.warn(_("abort: %s\n") % inst.strerror)
117 ui.warn(_("abort: %s\n") % inst.strerror)
118 except util.UnexpectedOutput, inst:
118 except util.UnexpectedOutput, inst:
119 ui.warn(_("abort: %s") % inst[0])
119 ui.warn(_("abort: %s") % inst[0])
120 if not isinstance(inst[1], basestring):
120 if not isinstance(inst[1], basestring):
121 ui.warn(" %r\n" % (inst[1],))
121 ui.warn(" %r\n" % (inst[1],))
122 elif not inst[1]:
122 elif not inst[1]:
123 ui.warn(_(" empty string\n"))
123 ui.warn(_(" empty string\n"))
124 else:
124 else:
125 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
125 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
126 except ImportError, inst:
126 except ImportError, inst:
127 m = str(inst).split()[-1]
127 m = str(inst).split()[-1]
128 ui.warn(_("abort: could not import module %s!\n") % m)
128 ui.warn(_("abort: could not import module %s!\n") % m)
129 if m in "mpatch bdiff".split():
129 if m in "mpatch bdiff".split():
130 ui.warn(_("(did you forget to compile extensions?)\n"))
130 ui.warn(_("(did you forget to compile extensions?)\n"))
131 elif m in "zlib".split():
131 elif m in "zlib".split():
132 ui.warn(_("(is your Python install correct?)\n"))
132 ui.warn(_("(is your Python install correct?)\n"))
133
133
134 except util.Abort, inst:
134 except util.Abort, inst:
135 ui.warn(_("abort: %s\n") % inst)
135 ui.warn(_("abort: %s\n") % inst)
136 except MemoryError:
136 except MemoryError:
137 ui.warn(_("abort: out of memory\n"))
137 ui.warn(_("abort: out of memory\n"))
138 except SystemExit, inst:
138 except SystemExit, inst:
139 # Commands shouldn't sys.exit directly, but give a return code.
139 # Commands shouldn't sys.exit directly, but give a return code.
140 # Just in case catch this and and pass exit code to caller.
140 # Just in case catch this and and pass exit code to caller.
141 return inst.code
141 return inst.code
142 except:
142 except:
143 ui.warn(_("** unknown exception encountered, details follow\n"))
143 ui.warn(_("** unknown exception encountered, details follow\n"))
144 ui.warn(_("** report bug details to "
144 ui.warn(_("** report bug details to "
145 "http://www.selenic.com/mercurial/bts\n"))
145 "http://www.selenic.com/mercurial/bts\n"))
146 ui.warn(_("** or mercurial@selenic.com\n"))
146 ui.warn(_("** or mercurial@selenic.com\n"))
147 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
147 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
148 % version.get_version())
148 % version.get_version())
149 ui.warn(_("** Extensions loaded: %s\n")
149 ui.warn(_("** Extensions loaded: %s\n")
150 % ", ".join([x[0] for x in extensions.extensions()]))
150 % ", ".join([x[0] for x in extensions.extensions()]))
151 raise
151 raise
152
152
153 return -1
153 return -1
154
154
155 def _findrepo(p):
155 def _findrepo(p):
156 while not os.path.isdir(os.path.join(p, ".hg")):
156 while not os.path.isdir(os.path.join(p, ".hg")):
157 oldp, p = p, os.path.dirname(p)
157 oldp, p = p, os.path.dirname(p)
158 if p == oldp:
158 if p == oldp:
159 return None
159 return None
160
160
161 return p
161 return p
162
162
163 def _parse(ui, args):
163 def _parse(ui, args):
164 options = {}
164 options = {}
165 cmdoptions = {}
165 cmdoptions = {}
166
166
167 try:
167 try:
168 args = fancyopts.fancyopts(args, commands.globalopts, options)
168 args = fancyopts.fancyopts(args, commands.globalopts, options)
169 except fancyopts.getopt.GetoptError, inst:
169 except fancyopts.getopt.GetoptError, inst:
170 raise ParseError(None, inst)
170 raise ParseError(None, inst)
171
171
172 if args:
172 if args:
173 cmd, args = args[0], args[1:]
173 cmd, args = args[0], args[1:]
174 aliases, i = cmdutil.findcmd(cmd, commands.table,
174 aliases, i = cmdutil.findcmd(cmd, commands.table,
175 ui.config("ui", "strict"))
175 ui.config("ui", "strict"))
176 cmd = aliases[0]
176 cmd = aliases[0]
177 defaults = ui.config("defaults", cmd)
177 defaults = ui.config("defaults", cmd)
178 if defaults:
178 if defaults:
179 args = shlex.split(defaults) + args
179 args = shlex.split(defaults) + args
180 c = list(i[1])
180 c = list(i[1])
181 else:
181 else:
182 cmd = None
182 cmd = None
183 c = []
183 c = []
184
184
185 # combine global options into local
185 # combine global options into local
186 for o in commands.globalopts:
186 for o in commands.globalopts:
187 c.append((o[0], o[1], options[o[1]], o[3]))
187 c.append((o[0], o[1], options[o[1]], o[3]))
188
188
189 try:
189 try:
190 args = fancyopts.fancyopts(args, c, cmdoptions)
190 args = fancyopts.fancyopts(args, c, cmdoptions)
191 except fancyopts.getopt.GetoptError, inst:
191 except fancyopts.getopt.GetoptError, inst:
192 raise ParseError(cmd, inst)
192 raise ParseError(cmd, inst)
193
193
194 # separate global options back out
194 # separate global options back out
195 for o in commands.globalopts:
195 for o in commands.globalopts:
196 n = o[1]
196 n = o[1]
197 options[n] = cmdoptions[n]
197 options[n] = cmdoptions[n]
198 del cmdoptions[n]
198 del cmdoptions[n]
199
199
200 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
200 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
201
201
202 def _parseconfig(config):
202 def _parseconfig(config):
203 """parse the --config options from the command line"""
203 """parse the --config options from the command line"""
204 parsed = []
204 parsed = []
205 for cfg in config:
205 for cfg in config:
206 try:
206 try:
207 name, value = cfg.split('=', 1)
207 name, value = cfg.split('=', 1)
208 section, name = name.split('.', 1)
208 section, name = name.split('.', 1)
209 if not section or not name:
209 if not section or not name:
210 raise IndexError
210 raise IndexError
211 parsed.append((section, name, value))
211 parsed.append((section, name, value))
212 except (IndexError, ValueError):
212 except (IndexError, ValueError):
213 raise util.Abort(_('malformed --config option: %s') % cfg)
213 raise util.Abort(_('malformed --config option: %s') % cfg)
214 return parsed
214 return parsed
215
215
216 def _earlygetopt(aliases, args):
216 def _earlygetopt(aliases, args):
217 """Return list of values for an option (or aliases).
217 """Return list of values for an option (or aliases).
218
218
219 The values are listed in the order they appear in args.
219 The values are listed in the order they appear in args.
220 The options and values are removed from args.
220 The options and values are removed from args.
221 """
221 """
222 try:
222 try:
223 argcount = args.index("--")
223 argcount = args.index("--")
224 except ValueError:
224 except ValueError:
225 argcount = len(args)
225 argcount = len(args)
226 shortopts = [opt for opt in aliases if len(opt) == 2]
226 shortopts = [opt for opt in aliases if len(opt) == 2]
227 values = []
227 values = []
228 pos = 0
228 pos = 0
229 while pos < argcount:
229 while pos < argcount:
230 if args[pos] in aliases:
230 if args[pos] in aliases:
231 if pos + 1 >= argcount:
231 if pos + 1 >= argcount:
232 # ignore and let getopt report an error if there is no value
232 # ignore and let getopt report an error if there is no value
233 break
233 break
234 del args[pos]
234 del args[pos]
235 values.append(args.pop(pos))
235 values.append(args.pop(pos))
236 argcount -= 2
236 argcount -= 2
237 elif args[pos][:2] in shortopts:
237 elif args[pos][:2] in shortopts:
238 # short option can have no following space, e.g. hg log -Rfoo
238 # short option can have no following space, e.g. hg log -Rfoo
239 values.append(args.pop(pos)[2:])
239 values.append(args.pop(pos)[2:])
240 argcount -= 1
240 argcount -= 1
241 else:
241 else:
242 pos += 1
242 pos += 1
243 return values
243 return values
244
244
245 _loaded = {}
245 _loaded = {}
246 def _dispatch(ui, args):
246 def _dispatch(ui, args):
247 # read --config before doing anything else
247 # read --config before doing anything else
248 # (e.g. to change trust settings for reading .hg/hgrc)
248 # (e.g. to change trust settings for reading .hg/hgrc)
249 config = _earlygetopt(['--config'], args)
249 config = _earlygetopt(['--config'], args)
250 if config:
250 if config:
251 ui.updateopts(config=_parseconfig(config))
251 ui.updateopts(config=_parseconfig(config))
252
252
253 # check for cwd
253 # check for cwd
254 cwd = _earlygetopt(['--cwd'], args)
254 cwd = _earlygetopt(['--cwd'], args)
255 if cwd:
255 if cwd:
256 os.chdir(cwd[-1])
256 os.chdir(cwd[-1])
257
257
258 # read the local repository .hgrc into a local ui object
258 # read the local repository .hgrc into a local ui object
259 path = _findrepo(os.getcwd()) or ""
259 path = _findrepo(os.getcwd()) or ""
260 if not path:
260 if not path:
261 lui = ui
261 lui = ui
262 if path:
262 if path:
263 try:
263 try:
264 lui = _ui.ui(parentui=ui)
264 lui = _ui.ui(parentui=ui)
265 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
265 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
266 except IOError:
266 except IOError:
267 pass
267 pass
268
268
269 # now we can expand paths, even ones in .hg/hgrc
269 # now we can expand paths, even ones in .hg/hgrc
270 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
270 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
271 if rpath:
271 if rpath:
272 path = lui.expandpath(rpath[-1])
272 path = lui.expandpath(rpath[-1])
273 lui = _ui.ui(parentui=ui)
273 lui = _ui.ui(parentui=ui)
274 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
274 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
275
275
276 extensions.loadall(lui)
276 extensions.loadall(lui)
277 for name, module in extensions.extensions():
277 for name, module in extensions.extensions():
278 if name in _loaded:
278 if name in _loaded:
279 continue
279 continue
280
280
281 # setup extensions
281 # setup extensions
282 # TODO this should be generalized to scheme, where extensions can
282 # TODO this should be generalized to scheme, where extensions can
283 # redepend on other extensions. then we should toposort them, and
283 # redepend on other extensions. then we should toposort them, and
284 # do initialization in correct order
284 # do initialization in correct order
285 extsetup = getattr(module, 'extsetup', None)
285 extsetup = getattr(module, 'extsetup', None)
286 if extsetup:
286 if extsetup:
287 extsetup()
287 extsetup()
288
288
289 cmdtable = getattr(module, 'cmdtable', {})
289 cmdtable = getattr(module, 'cmdtable', {})
290 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
290 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
291 if overrides:
291 if overrides:
292 ui.warn(_("extension '%s' overrides commands: %s\n")
292 ui.warn(_("extension '%s' overrides commands: %s\n")
293 % (name, " ".join(overrides)))
293 % (name, " ".join(overrides)))
294 commands.table.update(cmdtable)
294 commands.table.update(cmdtable)
295 _loaded[name] = 1
295 _loaded[name] = 1
296 # check for fallback encoding
296 # check for fallback encoding
297 fallback = lui.config('ui', 'fallbackencoding')
297 fallback = lui.config('ui', 'fallbackencoding')
298 if fallback:
298 if fallback:
299 util._fallbackencoding = fallback
299 util._fallbackencoding = fallback
300
300
301 fullargs = args
301 fullargs = args
302 cmd, func, args, options, cmdoptions = _parse(lui, args)
302 cmd, func, args, options, cmdoptions = _parse(lui, args)
303
303
304 if options["config"]:
304 if options["config"]:
305 raise util.Abort(_("Option --config may not be abbreviated!"))
305 raise util.Abort(_("Option --config may not be abbreviated!"))
306 if options["cwd"]:
306 if options["cwd"]:
307 raise util.Abort(_("Option --cwd may not be abbreviated!"))
307 raise util.Abort(_("Option --cwd may not be abbreviated!"))
308 if options["repository"]:
308 if options["repository"]:
309 raise util.Abort(_(
309 raise util.Abort(_(
310 "Option -R has to be separated from other options (i.e. not -qR) "
310 "Option -R has to be separated from other options (i.e. not -qR) "
311 "and --repository may only be abbreviated as --repo!"))
311 "and --repository may only be abbreviated as --repo!"))
312
312
313 if options["encoding"]:
313 if options["encoding"]:
314 util._encoding = options["encoding"]
314 util._encoding = options["encoding"]
315 if options["encodingmode"]:
315 if options["encodingmode"]:
316 util._encodingmode = options["encodingmode"]
316 util._encodingmode = options["encodingmode"]
317 if options["time"]:
317 if options["time"]:
318 def get_times():
318 def get_times():
319 t = os.times()
319 t = os.times()
320 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
320 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
321 t = (t[0], t[1], t[2], t[3], time.clock())
321 t = (t[0], t[1], t[2], t[3], time.clock())
322 return t
322 return t
323 s = get_times()
323 s = get_times()
324 def print_time():
324 def print_time():
325 t = get_times()
325 t = get_times()
326 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
326 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
327 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
327 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
328 atexit.register(print_time)
328 atexit.register(print_time)
329
329
330 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
330 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
331 not options["noninteractive"], options["traceback"])
331 not options["noninteractive"], options["traceback"])
332
332
333 if options['help']:
333 if options['help']:
334 return commands.help_(ui, cmd, options['version'])
334 return commands.help_(ui, cmd, options['version'])
335 elif options['version']:
335 elif options['version']:
336 return commands.version_(ui)
336 return commands.version_(ui)
337 elif not cmd:
337 elif not cmd:
338 return commands.help_(ui, 'shortlist')
338 return commands.help_(ui, 'shortlist')
339
339
340 repo = None
340 repo = None
341 if cmd not in commands.norepo.split():
341 if cmd not in commands.norepo.split():
342 try:
342 try:
343 repo = hg.repository(ui, path=path)
343 repo = hg.repository(ui, path=path)
344 ui = repo.ui
344 ui = repo.ui
345 if not repo.local():
345 if not repo.local():
346 raise util.Abort(_("repository '%s' is not local") % path)
346 raise util.Abort(_("repository '%s' is not local") % path)
347 ui.setconfig("bundle", "mainreporoot", repo.root)
347 ui.setconfig("bundle", "mainreporoot", repo.root)
348 except RepoError:
348 except RepoError:
349 if cmd not in commands.optionalrepo.split():
349 if cmd not in commands.optionalrepo.split():
350 if args and not path: # try to infer -R from command args
350 if args and not path: # try to infer -R from command args
351 repos = map(_findrepo, args)
351 repos = map(_findrepo, args)
352 guess = repos[0]
352 guess = repos[0]
353 if guess and repos.count(guess) == len(repos):
353 if guess and repos.count(guess) == len(repos):
354 return _dispatch(ui, ['--repository', guess] + fullargs)
354 return _dispatch(ui, ['--repository', guess] + fullargs)
355 if not path:
355 if not path:
356 raise RepoError(_("There is no Mercurial repository here"
356 raise RepoError(_("There is no Mercurial repository here"
357 " (.hg not found)"))
357 " (.hg not found)"))
358 raise
358 raise
359 d = lambda: func(ui, repo, *args, **cmdoptions)
359 args.insert(0, repo)
360 else:
360
361 d = lambda: func(ui, *args, **cmdoptions)
361 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
362
362
363 # run pre-hook, and abort if it fails
363 # run pre-hook, and abort if it fails
364 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
364 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
365 if ret:
365 if ret:
366 return ret
366 return ret
367 ret = _runcommand(ui, options, cmd, d)
367 ret = _runcommand(ui, options, cmd, d)
368 # run post-hook, passing command result
368 # run post-hook, passing command result
369 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
369 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
370 result = ret)
370 result = ret)
371 return ret
371 return ret
372
372
373 def _runcommand(ui, options, cmd, cmdfunc):
373 def _runcommand(ui, options, cmd, cmdfunc):
374 def checkargs():
374 def checkargs():
375 try:
375 try:
376 return cmdfunc()
376 return cmdfunc()
377 except TypeError:
377 except util.SignatureError:
378 # was this an argument error?
379 tb = traceback.extract_tb(sys.exc_info()[2])
380 if len(tb) != 2: # no
381 raise
382 raise ParseError(cmd, _("invalid arguments"))
378 raise ParseError(cmd, _("invalid arguments"))
383
379
384 if options['profile']:
380 if options['profile']:
385 import hotshot, hotshot.stats
381 import hotshot, hotshot.stats
386 prof = hotshot.Profile("hg.prof")
382 prof = hotshot.Profile("hg.prof")
387 try:
383 try:
388 try:
384 try:
389 return prof.runcall(checkargs)
385 return prof.runcall(checkargs)
390 except:
386 except:
391 try:
387 try:
392 ui.warn(_('exception raised - generating '
388 ui.warn(_('exception raised - generating '
393 'profile anyway\n'))
389 'profile anyway\n'))
394 except:
390 except:
395 pass
391 pass
396 raise
392 raise
397 finally:
393 finally:
398 prof.close()
394 prof.close()
399 stats = hotshot.stats.load("hg.prof")
395 stats = hotshot.stats.load("hg.prof")
400 stats.strip_dirs()
396 stats.strip_dirs()
401 stats.sort_stats('time', 'calls')
397 stats.sort_stats('time', 'calls')
402 stats.print_stats(40)
398 stats.print_stats(40)
403 elif options['lsprof']:
399 elif options['lsprof']:
404 try:
400 try:
405 from mercurial import lsprof
401 from mercurial import lsprof
406 except ImportError:
402 except ImportError:
407 raise util.Abort(_(
403 raise util.Abort(_(
408 'lsprof not available - install from '
404 'lsprof not available - install from '
409 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
405 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
410 p = lsprof.Profiler()
406 p = lsprof.Profiler()
411 p.enable(subcalls=True)
407 p.enable(subcalls=True)
412 try:
408 try:
413 return checkargs()
409 return checkargs()
414 finally:
410 finally:
415 p.disable()
411 p.disable()
416 stats = lsprof.Stats(p.getstats())
412 stats = lsprof.Stats(p.getstats())
417 stats.sort()
413 stats.sort()
418 stats.pprint(top=10, file=sys.stderr, climit=5)
414 stats.pprint(top=10, file=sys.stderr, climit=5)
419 else:
415 else:
420 return checkargs()
416 return checkargs()
@@ -1,115 +1,116
1 # extensions.py - extension handling for mercurial
1 # extensions.py - extension handling 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import imp, os
8 import imp, os
9 import util, cmdutil
9 import util, cmdutil
10 from i18n import _
10 from i18n import _
11
11
12 _extensions = {}
12 _extensions = {}
13 _order = []
13 _order = []
14
14
15 def extensions():
15 def extensions():
16 for name in _order:
16 for name in _order:
17 module = _extensions[name]
17 module = _extensions[name]
18 if module:
18 if module:
19 yield name, module
19 yield name, module
20
20
21 def find(name):
21 def find(name):
22 '''return module with given extension name'''
22 '''return module with given extension name'''
23 try:
23 try:
24 return _extensions[name]
24 return _extensions[name]
25 except KeyError:
25 except KeyError:
26 for k, v in _extensions.iteritems():
26 for k, v in _extensions.iteritems():
27 if k.endswith('.' + name) or k.endswith('/' + name):
27 if k.endswith('.' + name) or k.endswith('/' + name):
28 return v
28 return v
29 raise KeyError(name)
29 raise KeyError(name)
30
30
31 def load(ui, name, path):
31 def load(ui, name, path):
32 if name.startswith('hgext.') or name.startswith('hgext/'):
32 if name.startswith('hgext.') or name.startswith('hgext/'):
33 shortname = name[6:]
33 shortname = name[6:]
34 else:
34 else:
35 shortname = name
35 shortname = name
36 if shortname in _extensions:
36 if shortname in _extensions:
37 return
37 return
38 _extensions[shortname] = None
38 _extensions[shortname] = None
39 if path:
39 if path:
40 # the module will be loaded in sys.modules
40 # the module will be loaded in sys.modules
41 # choose an unique name so that it doesn't
41 # choose an unique name so that it doesn't
42 # conflicts with other modules
42 # conflicts with other modules
43 module_name = "hgext_%s" % name.replace('.', '_')
43 module_name = "hgext_%s" % name.replace('.', '_')
44 if os.path.isdir(path):
44 if os.path.isdir(path):
45 # module/__init__.py style
45 # module/__init__.py style
46 d, f = os.path.split(path)
46 d, f = os.path.split(path)
47 fd, fpath, desc = imp.find_module(f, [d])
47 fd, fpath, desc = imp.find_module(f, [d])
48 mod = imp.load_module(module_name, fd, fpath, desc)
48 mod = imp.load_module(module_name, fd, fpath, desc)
49 else:
49 else:
50 mod = imp.load_source(module_name, path)
50 mod = imp.load_source(module_name, path)
51 else:
51 else:
52 def importh(name):
52 def importh(name):
53 mod = __import__(name)
53 mod = __import__(name)
54 components = name.split('.')
54 components = name.split('.')
55 for comp in components[1:]:
55 for comp in components[1:]:
56 mod = getattr(mod, comp)
56 mod = getattr(mod, comp)
57 return mod
57 return mod
58 try:
58 try:
59 mod = importh("hgext.%s" % name)
59 mod = importh("hgext.%s" % name)
60 except ImportError:
60 except ImportError:
61 mod = importh(name)
61 mod = importh(name)
62 _extensions[shortname] = mod
62 _extensions[shortname] = mod
63 _order.append(shortname)
63 _order.append(shortname)
64
64
65 uisetup = getattr(mod, 'uisetup', None)
65 uisetup = getattr(mod, 'uisetup', None)
66 if uisetup:
66 if uisetup:
67 uisetup(ui)
67 uisetup(ui)
68
68
69 def loadall(ui):
69 def loadall(ui):
70 result = ui.configitems("extensions")
70 result = ui.configitems("extensions")
71 for i, (name, path) in enumerate(result):
71 for i, (name, path) in enumerate(result):
72 if path:
72 if path:
73 if path[0] == '!':
73 if path[0] == '!':
74 continue
74 continue
75 path = os.path.expanduser(path)
75 path = os.path.expanduser(path)
76 try:
76 try:
77 load(ui, name, path)
77 load(ui, name, path)
78 except (util.SignalInterrupt, KeyboardInterrupt):
78 except (util.SignalInterrupt, KeyboardInterrupt):
79 raise
79 raise
80 except Exception, inst:
80 except Exception, inst:
81 if path:
81 if path:
82 ui.warn(_("*** failed to import extension %s from %s: %s\n")
82 ui.warn(_("*** failed to import extension %s from %s: %s\n")
83 % (name, path, inst))
83 % (name, path, inst))
84 else:
84 else:
85 ui.warn(_("*** failed to import extension %s: %s\n")
85 ui.warn(_("*** failed to import extension %s: %s\n")
86 % (name, inst))
86 % (name, inst))
87 if ui.print_exc():
87 if ui.print_exc():
88 return 1
88 return 1
89
89
90 def wrapcommand(table, command, wrapper):
90 def wrapcommand(table, command, wrapper):
91 aliases, entry = cmdutil.findcmd(command, table)
91 aliases, entry = cmdutil.findcmd(command, table)
92 for alias, e in table.iteritems():
92 for alias, e in table.iteritems():
93 if e is entry:
93 if e is entry:
94 key = alias
94 key = alias
95 break
95 break
96
96
97 origfn = entry[0]
97 origfn = entry[0]
98 def wrap(*args, **kwargs):
98 def wrap(*args, **kwargs):
99 return wrapper(origfn, *args, **kwargs)
99 return util.checksignature(wrapper)(
100 util.checksignature(origfn), *args, **kwargs)
100
101
101 wrap.__doc__ = getattr(origfn, '__doc__')
102 wrap.__doc__ = getattr(origfn, '__doc__')
102 wrap.__module__ = getattr(origfn, '__module__')
103 wrap.__module__ = getattr(origfn, '__module__')
103
104
104 newentry = list(entry)
105 newentry = list(entry)
105 newentry[0] = wrap
106 newentry[0] = wrap
106 table[key] = tuple(newentry)
107 table[key] = tuple(newentry)
107 return entry
108 return entry
108
109
109 def wrapfunction(container, funcname, wrapper):
110 def wrapfunction(container, funcname, wrapper):
110 def wrap(*args, **kwargs):
111 def wrap(*args, **kwargs):
111 return wrapper(origfn, *args, **kwargs)
112 return wrapper(origfn, *args, **kwargs)
112
113
113 origfn = getattr(container, funcname)
114 origfn = getattr(container, funcname)
114 setattr(container, funcname, wrap)
115 setattr(container, funcname, wrap)
115 return origfn
116 return origfn
@@ -1,1934 +1,1949
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp
18 import imp
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 subprocess.Popen # trigger ImportError early
54 subprocess.Popen # trigger ImportError early
55 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
56 def popen2(cmd, mode='t', bufsize=-1):
56 def popen2(cmd, mode='t', bufsize=-1):
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 close_fds=closefds,
58 close_fds=closefds,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 return p.stdin, p.stdout
60 return p.stdin, p.stdout
61 def popen3(cmd, mode='t', bufsize=-1):
61 def popen3(cmd, mode='t', bufsize=-1):
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 close_fds=closefds,
63 close_fds=closefds,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stderr=subprocess.PIPE)
65 stderr=subprocess.PIPE)
66 return p.stdin, p.stdout, p.stderr
66 return p.stdin, p.stdout, p.stderr
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 stderr = capturestderr and subprocess.PIPE or None
68 stderr = capturestderr and subprocess.PIPE or None
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 close_fds=closefds,
70 close_fds=closefds,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 stderr=stderr)
72 stderr=stderr)
73 p.fromchild = p.stdout
73 p.fromchild = p.stdout
74 p.tochild = p.stdin
74 p.tochild = p.stdin
75 p.childerr = p.stderr
75 p.childerr = p.stderr
76 return p
76 return p
77 except ImportError:
77 except ImportError:
78 subprocess = None
78 subprocess = None
79 from popen2 import Popen3
79 from popen2 import Popen3
80 popen2 = os.popen2
80 popen2 = os.popen2
81 popen3 = os.popen3
81 popen3 = os.popen3
82
82
83
83
84 try:
84 try:
85 _encoding = os.environ.get("HGENCODING")
85 _encoding = os.environ.get("HGENCODING")
86 if sys.platform == 'darwin' and not _encoding:
86 if sys.platform == 'darwin' and not _encoding:
87 # On darwin, getpreferredencoding ignores the locale environment and
87 # On darwin, getpreferredencoding ignores the locale environment and
88 # always returns mac-roman. We override this if the environment is
88 # always returns mac-roman. We override this if the environment is
89 # not C (has been customized by the user).
89 # not C (has been customized by the user).
90 locale.setlocale(locale.LC_CTYPE, '')
90 locale.setlocale(locale.LC_CTYPE, '')
91 _encoding = locale.getlocale()[1]
91 _encoding = locale.getlocale()[1]
92 if not _encoding:
92 if not _encoding:
93 _encoding = locale.getpreferredencoding() or 'ascii'
93 _encoding = locale.getpreferredencoding() or 'ascii'
94 except locale.Error:
94 except locale.Error:
95 _encoding = 'ascii'
95 _encoding = 'ascii'
96 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
96 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
97 _fallbackencoding = 'ISO-8859-1'
97 _fallbackencoding = 'ISO-8859-1'
98
98
99 def tolocal(s):
99 def tolocal(s):
100 """
100 """
101 Convert a string from internal UTF-8 to local encoding
101 Convert a string from internal UTF-8 to local encoding
102
102
103 All internal strings should be UTF-8 but some repos before the
103 All internal strings should be UTF-8 but some repos before the
104 implementation of locale support may contain latin1 or possibly
104 implementation of locale support may contain latin1 or possibly
105 other character sets. We attempt to decode everything strictly
105 other character sets. We attempt to decode everything strictly
106 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
106 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
107 replace unknown characters.
107 replace unknown characters.
108 """
108 """
109 for e in ('UTF-8', _fallbackencoding):
109 for e in ('UTF-8', _fallbackencoding):
110 try:
110 try:
111 u = s.decode(e) # attempt strict decoding
111 u = s.decode(e) # attempt strict decoding
112 return u.encode(_encoding, "replace")
112 return u.encode(_encoding, "replace")
113 except LookupError, k:
113 except LookupError, k:
114 raise Abort(_("%s, please check your locale settings") % k)
114 raise Abort(_("%s, please check your locale settings") % k)
115 except UnicodeDecodeError:
115 except UnicodeDecodeError:
116 pass
116 pass
117 u = s.decode("utf-8", "replace") # last ditch
117 u = s.decode("utf-8", "replace") # last ditch
118 return u.encode(_encoding, "replace")
118 return u.encode(_encoding, "replace")
119
119
120 def fromlocal(s):
120 def fromlocal(s):
121 """
121 """
122 Convert a string from the local character encoding to UTF-8
122 Convert a string from the local character encoding to UTF-8
123
123
124 We attempt to decode strings using the encoding mode set by
124 We attempt to decode strings using the encoding mode set by
125 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
125 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
126 characters will cause an error message. Other modes include
126 characters will cause an error message. Other modes include
127 'replace', which replaces unknown characters with a special
127 'replace', which replaces unknown characters with a special
128 Unicode character, and 'ignore', which drops the character.
128 Unicode character, and 'ignore', which drops the character.
129 """
129 """
130 try:
130 try:
131 return s.decode(_encoding, _encodingmode).encode("utf-8")
131 return s.decode(_encoding, _encodingmode).encode("utf-8")
132 except UnicodeDecodeError, inst:
132 except UnicodeDecodeError, inst:
133 sub = s[max(0, inst.start-10):inst.start+10]
133 sub = s[max(0, inst.start-10):inst.start+10]
134 raise Abort("decoding near '%s': %s!" % (sub, inst))
134 raise Abort("decoding near '%s': %s!" % (sub, inst))
135 except LookupError, k:
135 except LookupError, k:
136 raise Abort(_("%s, please check your locale settings") % k)
136 raise Abort(_("%s, please check your locale settings") % k)
137
137
138 def locallen(s):
138 def locallen(s):
139 """Find the length in characters of a local string"""
139 """Find the length in characters of a local string"""
140 return len(s.decode(_encoding, "replace"))
140 return len(s.decode(_encoding, "replace"))
141
141
142 # used by parsedate
142 # used by parsedate
143 defaultdateformats = (
143 defaultdateformats = (
144 '%Y-%m-%d %H:%M:%S',
144 '%Y-%m-%d %H:%M:%S',
145 '%Y-%m-%d %I:%M:%S%p',
145 '%Y-%m-%d %I:%M:%S%p',
146 '%Y-%m-%d %H:%M',
146 '%Y-%m-%d %H:%M',
147 '%Y-%m-%d %I:%M%p',
147 '%Y-%m-%d %I:%M%p',
148 '%Y-%m-%d',
148 '%Y-%m-%d',
149 '%m-%d',
149 '%m-%d',
150 '%m/%d',
150 '%m/%d',
151 '%m/%d/%y',
151 '%m/%d/%y',
152 '%m/%d/%Y',
152 '%m/%d/%Y',
153 '%a %b %d %H:%M:%S %Y',
153 '%a %b %d %H:%M:%S %Y',
154 '%a %b %d %I:%M:%S%p %Y',
154 '%a %b %d %I:%M:%S%p %Y',
155 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
155 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
156 '%b %d %H:%M:%S %Y',
156 '%b %d %H:%M:%S %Y',
157 '%b %d %I:%M:%S%p %Y',
157 '%b %d %I:%M:%S%p %Y',
158 '%b %d %H:%M:%S',
158 '%b %d %H:%M:%S',
159 '%b %d %I:%M:%S%p',
159 '%b %d %I:%M:%S%p',
160 '%b %d %H:%M',
160 '%b %d %H:%M',
161 '%b %d %I:%M%p',
161 '%b %d %I:%M%p',
162 '%b %d %Y',
162 '%b %d %Y',
163 '%b %d',
163 '%b %d',
164 '%H:%M:%S',
164 '%H:%M:%S',
165 '%I:%M:%SP',
165 '%I:%M:%SP',
166 '%H:%M',
166 '%H:%M',
167 '%I:%M%p',
167 '%I:%M%p',
168 )
168 )
169
169
170 extendeddateformats = defaultdateformats + (
170 extendeddateformats = defaultdateformats + (
171 "%Y",
171 "%Y",
172 "%Y-%m",
172 "%Y-%m",
173 "%b",
173 "%b",
174 "%b %Y",
174 "%b %Y",
175 )
175 )
176
176
177 class SignalInterrupt(Exception):
177 class SignalInterrupt(Exception):
178 """Exception raised on SIGTERM and SIGHUP."""
178 """Exception raised on SIGTERM and SIGHUP."""
179
179
180 # differences from SafeConfigParser:
180 # differences from SafeConfigParser:
181 # - case-sensitive keys
181 # - case-sensitive keys
182 # - allows values that are not strings (this means that you may not
182 # - allows values that are not strings (this means that you may not
183 # be able to save the configuration to a file)
183 # be able to save the configuration to a file)
184 class configparser(ConfigParser.SafeConfigParser):
184 class configparser(ConfigParser.SafeConfigParser):
185 def optionxform(self, optionstr):
185 def optionxform(self, optionstr):
186 return optionstr
186 return optionstr
187
187
188 def set(self, section, option, value):
188 def set(self, section, option, value):
189 return ConfigParser.ConfigParser.set(self, section, option, value)
189 return ConfigParser.ConfigParser.set(self, section, option, value)
190
190
191 def _interpolate(self, section, option, rawval, vars):
191 def _interpolate(self, section, option, rawval, vars):
192 if not isinstance(rawval, basestring):
192 if not isinstance(rawval, basestring):
193 return rawval
193 return rawval
194 return ConfigParser.SafeConfigParser._interpolate(self, section,
194 return ConfigParser.SafeConfigParser._interpolate(self, section,
195 option, rawval, vars)
195 option, rawval, vars)
196
196
197 def cachefunc(func):
197 def cachefunc(func):
198 '''cache the result of function calls'''
198 '''cache the result of function calls'''
199 # XXX doesn't handle keywords args
199 # XXX doesn't handle keywords args
200 cache = {}
200 cache = {}
201 if func.func_code.co_argcount == 1:
201 if func.func_code.co_argcount == 1:
202 # we gain a small amount of time because
202 # we gain a small amount of time because
203 # we don't need to pack/unpack the list
203 # we don't need to pack/unpack the list
204 def f(arg):
204 def f(arg):
205 if arg not in cache:
205 if arg not in cache:
206 cache[arg] = func(arg)
206 cache[arg] = func(arg)
207 return cache[arg]
207 return cache[arg]
208 else:
208 else:
209 def f(*args):
209 def f(*args):
210 if args not in cache:
210 if args not in cache:
211 cache[args] = func(*args)
211 cache[args] = func(*args)
212 return cache[args]
212 return cache[args]
213
213
214 return f
214 return f
215
215
216 def pipefilter(s, cmd):
216 def pipefilter(s, cmd):
217 '''filter string S through command CMD, returning its output'''
217 '''filter string S through command CMD, returning its output'''
218 (pin, pout) = popen2(cmd, 'b')
218 (pin, pout) = popen2(cmd, 'b')
219 def writer():
219 def writer():
220 try:
220 try:
221 pin.write(s)
221 pin.write(s)
222 pin.close()
222 pin.close()
223 except IOError, inst:
223 except IOError, inst:
224 if inst.errno != errno.EPIPE:
224 if inst.errno != errno.EPIPE:
225 raise
225 raise
226
226
227 # we should use select instead on UNIX, but this will work on most
227 # we should use select instead on UNIX, but this will work on most
228 # systems, including Windows
228 # systems, including Windows
229 w = threading.Thread(target=writer)
229 w = threading.Thread(target=writer)
230 w.start()
230 w.start()
231 f = pout.read()
231 f = pout.read()
232 pout.close()
232 pout.close()
233 w.join()
233 w.join()
234 return f
234 return f
235
235
236 def tempfilter(s, cmd):
236 def tempfilter(s, cmd):
237 '''filter string S through a pair of temporary files with CMD.
237 '''filter string S through a pair of temporary files with CMD.
238 CMD is used as a template to create the real command to be run,
238 CMD is used as a template to create the real command to be run,
239 with the strings INFILE and OUTFILE replaced by the real names of
239 with the strings INFILE and OUTFILE replaced by the real names of
240 the temporary files generated.'''
240 the temporary files generated.'''
241 inname, outname = None, None
241 inname, outname = None, None
242 try:
242 try:
243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
244 fp = os.fdopen(infd, 'wb')
244 fp = os.fdopen(infd, 'wb')
245 fp.write(s)
245 fp.write(s)
246 fp.close()
246 fp.close()
247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
248 os.close(outfd)
248 os.close(outfd)
249 cmd = cmd.replace('INFILE', inname)
249 cmd = cmd.replace('INFILE', inname)
250 cmd = cmd.replace('OUTFILE', outname)
250 cmd = cmd.replace('OUTFILE', outname)
251 code = os.system(cmd)
251 code = os.system(cmd)
252 if sys.platform == 'OpenVMS' and code & 1:
252 if sys.platform == 'OpenVMS' and code & 1:
253 code = 0
253 code = 0
254 if code: raise Abort(_("command '%s' failed: %s") %
254 if code: raise Abort(_("command '%s' failed: %s") %
255 (cmd, explain_exit(code)))
255 (cmd, explain_exit(code)))
256 return open(outname, 'rb').read()
256 return open(outname, 'rb').read()
257 finally:
257 finally:
258 try:
258 try:
259 if inname: os.unlink(inname)
259 if inname: os.unlink(inname)
260 except: pass
260 except: pass
261 try:
261 try:
262 if outname: os.unlink(outname)
262 if outname: os.unlink(outname)
263 except: pass
263 except: pass
264
264
265 filtertable = {
265 filtertable = {
266 'tempfile:': tempfilter,
266 'tempfile:': tempfilter,
267 'pipe:': pipefilter,
267 'pipe:': pipefilter,
268 }
268 }
269
269
270 def filter(s, cmd):
270 def filter(s, cmd):
271 "filter a string through a command that transforms its input to its output"
271 "filter a string through a command that transforms its input to its output"
272 for name, fn in filtertable.iteritems():
272 for name, fn in filtertable.iteritems():
273 if cmd.startswith(name):
273 if cmd.startswith(name):
274 return fn(s, cmd[len(name):].lstrip())
274 return fn(s, cmd[len(name):].lstrip())
275 return pipefilter(s, cmd)
275 return pipefilter(s, cmd)
276
276
277 def binary(s):
277 def binary(s):
278 """return true if a string is binary data"""
278 """return true if a string is binary data"""
279 if s and '\0' in s:
279 if s and '\0' in s:
280 return True
280 return True
281 return False
281 return False
282
282
283 def unique(g):
283 def unique(g):
284 """return the uniq elements of iterable g"""
284 """return the uniq elements of iterable g"""
285 return dict.fromkeys(g).keys()
285 return dict.fromkeys(g).keys()
286
286
287 def sort(l):
287 def sort(l):
288 if not isinstance(l, list):
288 if not isinstance(l, list):
289 l = list(l)
289 l = list(l)
290 l.sort()
290 l.sort()
291 return l
291 return l
292
292
293 class Abort(Exception):
293 class Abort(Exception):
294 """Raised if a command needs to print an error and exit."""
294 """Raised if a command needs to print an error and exit."""
295
295
296 class UnexpectedOutput(Abort):
296 class UnexpectedOutput(Abort):
297 """Raised to print an error with part of output and exit."""
297 """Raised to print an error with part of output and exit."""
298
298
299 def always(fn): return True
299 def always(fn): return True
300 def never(fn): return False
300 def never(fn): return False
301
301
302 def expand_glob(pats):
302 def expand_glob(pats):
303 '''On Windows, expand the implicit globs in a list of patterns'''
303 '''On Windows, expand the implicit globs in a list of patterns'''
304 if os.name != 'nt':
304 if os.name != 'nt':
305 return list(pats)
305 return list(pats)
306 ret = []
306 ret = []
307 for p in pats:
307 for p in pats:
308 kind, name = patkind(p, None)
308 kind, name = patkind(p, None)
309 if kind is None:
309 if kind is None:
310 globbed = glob.glob(name)
310 globbed = glob.glob(name)
311 if globbed:
311 if globbed:
312 ret.extend(globbed)
312 ret.extend(globbed)
313 continue
313 continue
314 # if we couldn't expand the glob, just keep it around
314 # if we couldn't expand the glob, just keep it around
315 ret.append(p)
315 ret.append(p)
316 return ret
316 return ret
317
317
318 def patkind(name, default):
318 def patkind(name, default):
319 """Split a string into an optional pattern kind prefix and the
319 """Split a string into an optional pattern kind prefix and the
320 actual pattern."""
320 actual pattern."""
321 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
321 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
322 if name.startswith(prefix + ':'): return name.split(':', 1)
322 if name.startswith(prefix + ':'): return name.split(':', 1)
323 return default, name
323 return default, name
324
324
325 def globre(pat, head='^', tail='$'):
325 def globre(pat, head='^', tail='$'):
326 "convert a glob pattern into a regexp"
326 "convert a glob pattern into a regexp"
327 i, n = 0, len(pat)
327 i, n = 0, len(pat)
328 res = ''
328 res = ''
329 group = 0
329 group = 0
330 def peek(): return i < n and pat[i]
330 def peek(): return i < n and pat[i]
331 while i < n:
331 while i < n:
332 c = pat[i]
332 c = pat[i]
333 i = i+1
333 i = i+1
334 if c == '*':
334 if c == '*':
335 if peek() == '*':
335 if peek() == '*':
336 i += 1
336 i += 1
337 res += '.*'
337 res += '.*'
338 else:
338 else:
339 res += '[^/]*'
339 res += '[^/]*'
340 elif c == '?':
340 elif c == '?':
341 res += '.'
341 res += '.'
342 elif c == '[':
342 elif c == '[':
343 j = i
343 j = i
344 if j < n and pat[j] in '!]':
344 if j < n and pat[j] in '!]':
345 j += 1
345 j += 1
346 while j < n and pat[j] != ']':
346 while j < n and pat[j] != ']':
347 j += 1
347 j += 1
348 if j >= n:
348 if j >= n:
349 res += '\\['
349 res += '\\['
350 else:
350 else:
351 stuff = pat[i:j].replace('\\','\\\\')
351 stuff = pat[i:j].replace('\\','\\\\')
352 i = j + 1
352 i = j + 1
353 if stuff[0] == '!':
353 if stuff[0] == '!':
354 stuff = '^' + stuff[1:]
354 stuff = '^' + stuff[1:]
355 elif stuff[0] == '^':
355 elif stuff[0] == '^':
356 stuff = '\\' + stuff
356 stuff = '\\' + stuff
357 res = '%s[%s]' % (res, stuff)
357 res = '%s[%s]' % (res, stuff)
358 elif c == '{':
358 elif c == '{':
359 group += 1
359 group += 1
360 res += '(?:'
360 res += '(?:'
361 elif c == '}' and group:
361 elif c == '}' and group:
362 res += ')'
362 res += ')'
363 group -= 1
363 group -= 1
364 elif c == ',' and group:
364 elif c == ',' and group:
365 res += '|'
365 res += '|'
366 elif c == '\\':
366 elif c == '\\':
367 p = peek()
367 p = peek()
368 if p:
368 if p:
369 i += 1
369 i += 1
370 res += re.escape(p)
370 res += re.escape(p)
371 else:
371 else:
372 res += re.escape(c)
372 res += re.escape(c)
373 else:
373 else:
374 res += re.escape(c)
374 res += re.escape(c)
375 return head + res + tail
375 return head + res + tail
376
376
377 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
377 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
378
378
379 def pathto(root, n1, n2):
379 def pathto(root, n1, n2):
380 '''return the relative path from one place to another.
380 '''return the relative path from one place to another.
381 root should use os.sep to separate directories
381 root should use os.sep to separate directories
382 n1 should use os.sep to separate directories
382 n1 should use os.sep to separate directories
383 n2 should use "/" to separate directories
383 n2 should use "/" to separate directories
384 returns an os.sep-separated path.
384 returns an os.sep-separated path.
385
385
386 If n1 is a relative path, it's assumed it's
386 If n1 is a relative path, it's assumed it's
387 relative to root.
387 relative to root.
388 n2 should always be relative to root.
388 n2 should always be relative to root.
389 '''
389 '''
390 if not n1: return localpath(n2)
390 if not n1: return localpath(n2)
391 if os.path.isabs(n1):
391 if os.path.isabs(n1):
392 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
392 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
393 return os.path.join(root, localpath(n2))
393 return os.path.join(root, localpath(n2))
394 n2 = '/'.join((pconvert(root), n2))
394 n2 = '/'.join((pconvert(root), n2))
395 a, b = splitpath(n1), n2.split('/')
395 a, b = splitpath(n1), n2.split('/')
396 a.reverse()
396 a.reverse()
397 b.reverse()
397 b.reverse()
398 while a and b and a[-1] == b[-1]:
398 while a and b and a[-1] == b[-1]:
399 a.pop()
399 a.pop()
400 b.pop()
400 b.pop()
401 b.reverse()
401 b.reverse()
402 return os.sep.join((['..'] * len(a)) + b) or '.'
402 return os.sep.join((['..'] * len(a)) + b) or '.'
403
403
404 def canonpath(root, cwd, myname):
404 def canonpath(root, cwd, myname):
405 """return the canonical path of myname, given cwd and root"""
405 """return the canonical path of myname, given cwd and root"""
406 if root == os.sep:
406 if root == os.sep:
407 rootsep = os.sep
407 rootsep = os.sep
408 elif endswithsep(root):
408 elif endswithsep(root):
409 rootsep = root
409 rootsep = root
410 else:
410 else:
411 rootsep = root + os.sep
411 rootsep = root + os.sep
412 name = myname
412 name = myname
413 if not os.path.isabs(name):
413 if not os.path.isabs(name):
414 name = os.path.join(root, cwd, name)
414 name = os.path.join(root, cwd, name)
415 name = os.path.normpath(name)
415 name = os.path.normpath(name)
416 audit_path = path_auditor(root)
416 audit_path = path_auditor(root)
417 if name != rootsep and name.startswith(rootsep):
417 if name != rootsep and name.startswith(rootsep):
418 name = name[len(rootsep):]
418 name = name[len(rootsep):]
419 audit_path(name)
419 audit_path(name)
420 return pconvert(name)
420 return pconvert(name)
421 elif name == root:
421 elif name == root:
422 return ''
422 return ''
423 else:
423 else:
424 # Determine whether `name' is in the hierarchy at or beneath `root',
424 # Determine whether `name' is in the hierarchy at or beneath `root',
425 # by iterating name=dirname(name) until that causes no change (can't
425 # by iterating name=dirname(name) until that causes no change (can't
426 # check name == '/', because that doesn't work on windows). For each
426 # check name == '/', because that doesn't work on windows). For each
427 # `name', compare dev/inode numbers. If they match, the list `rel'
427 # `name', compare dev/inode numbers. If they match, the list `rel'
428 # holds the reversed list of components making up the relative file
428 # holds the reversed list of components making up the relative file
429 # name we want.
429 # name we want.
430 root_st = os.stat(root)
430 root_st = os.stat(root)
431 rel = []
431 rel = []
432 while True:
432 while True:
433 try:
433 try:
434 name_st = os.stat(name)
434 name_st = os.stat(name)
435 except OSError:
435 except OSError:
436 break
436 break
437 if samestat(name_st, root_st):
437 if samestat(name_st, root_st):
438 if not rel:
438 if not rel:
439 # name was actually the same as root (maybe a symlink)
439 # name was actually the same as root (maybe a symlink)
440 return ''
440 return ''
441 rel.reverse()
441 rel.reverse()
442 name = os.path.join(*rel)
442 name = os.path.join(*rel)
443 audit_path(name)
443 audit_path(name)
444 return pconvert(name)
444 return pconvert(name)
445 dirname, basename = os.path.split(name)
445 dirname, basename = os.path.split(name)
446 rel.append(basename)
446 rel.append(basename)
447 if dirname == name:
447 if dirname == name:
448 break
448 break
449 name = dirname
449 name = dirname
450
450
451 raise Abort('%s not under root' % myname)
451 raise Abort('%s not under root' % myname)
452
452
453 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
453 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
454 """build a function to match a set of file patterns
454 """build a function to match a set of file patterns
455
455
456 arguments:
456 arguments:
457 canonroot - the canonical root of the tree you're matching against
457 canonroot - the canonical root of the tree you're matching against
458 cwd - the current working directory, if relevant
458 cwd - the current working directory, if relevant
459 names - patterns to find
459 names - patterns to find
460 inc - patterns to include
460 inc - patterns to include
461 exc - patterns to exclude
461 exc - patterns to exclude
462 dflt_pat - if a pattern in names has no explicit type, assume this one
462 dflt_pat - if a pattern in names has no explicit type, assume this one
463 src - where these patterns came from (e.g. .hgignore)
463 src - where these patterns came from (e.g. .hgignore)
464
464
465 a pattern is one of:
465 a pattern is one of:
466 'glob:<glob>' - a glob relative to cwd
466 'glob:<glob>' - a glob relative to cwd
467 're:<regexp>' - a regular expression
467 're:<regexp>' - a regular expression
468 'path:<path>' - a path relative to canonroot
468 'path:<path>' - a path relative to canonroot
469 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
469 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
470 'relpath:<path>' - a path relative to cwd
470 'relpath:<path>' - a path relative to cwd
471 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
471 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
472 '<something>' - one of the cases above, selected by the dflt_pat argument
472 '<something>' - one of the cases above, selected by the dflt_pat argument
473
473
474 returns:
474 returns:
475 a 3-tuple containing
475 a 3-tuple containing
476 - list of roots (places where one should start a recursive walk of the fs);
476 - list of roots (places where one should start a recursive walk of the fs);
477 this often matches the explicit non-pattern names passed in, but also
477 this often matches the explicit non-pattern names passed in, but also
478 includes the initial part of glob: patterns that has no glob characters
478 includes the initial part of glob: patterns that has no glob characters
479 - a bool match(filename) function
479 - a bool match(filename) function
480 - a bool indicating if any patterns were passed in
480 - a bool indicating if any patterns were passed in
481 """
481 """
482
482
483 # a common case: no patterns at all
483 # a common case: no patterns at all
484 if not names and not inc and not exc:
484 if not names and not inc and not exc:
485 return [], always, False
485 return [], always, False
486
486
487 def contains_glob(name):
487 def contains_glob(name):
488 for c in name:
488 for c in name:
489 if c in _globchars: return True
489 if c in _globchars: return True
490 return False
490 return False
491
491
492 def regex(kind, name, tail):
492 def regex(kind, name, tail):
493 '''convert a pattern into a regular expression'''
493 '''convert a pattern into a regular expression'''
494 if not name:
494 if not name:
495 return ''
495 return ''
496 if kind == 're':
496 if kind == 're':
497 return name
497 return name
498 elif kind == 'path':
498 elif kind == 'path':
499 return '^' + re.escape(name) + '(?:/|$)'
499 return '^' + re.escape(name) + '(?:/|$)'
500 elif kind == 'relglob':
500 elif kind == 'relglob':
501 return globre(name, '(?:|.*/)', tail)
501 return globre(name, '(?:|.*/)', tail)
502 elif kind == 'relpath':
502 elif kind == 'relpath':
503 return re.escape(name) + '(?:/|$)'
503 return re.escape(name) + '(?:/|$)'
504 elif kind == 'relre':
504 elif kind == 'relre':
505 if name.startswith('^'):
505 if name.startswith('^'):
506 return name
506 return name
507 return '.*' + name
507 return '.*' + name
508 return globre(name, '', tail)
508 return globre(name, '', tail)
509
509
510 def matchfn(pats, tail):
510 def matchfn(pats, tail):
511 """build a matching function from a set of patterns"""
511 """build a matching function from a set of patterns"""
512 if not pats:
512 if not pats:
513 return
513 return
514 try:
514 try:
515 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
515 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
516 if len(pat) > 20000:
516 if len(pat) > 20000:
517 raise OverflowError()
517 raise OverflowError()
518 return re.compile(pat).match
518 return re.compile(pat).match
519 except OverflowError:
519 except OverflowError:
520 # We're using a Python with a tiny regex engine and we
520 # We're using a Python with a tiny regex engine and we
521 # made it explode, so we'll divide the pattern list in two
521 # made it explode, so we'll divide the pattern list in two
522 # until it works
522 # until it works
523 l = len(pats)
523 l = len(pats)
524 if l < 2:
524 if l < 2:
525 raise
525 raise
526 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
526 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
527 return lambda s: a(s) or b(s)
527 return lambda s: a(s) or b(s)
528 except re.error:
528 except re.error:
529 for k, p in pats:
529 for k, p in pats:
530 try:
530 try:
531 re.compile('(?:%s)' % regex(k, p, tail))
531 re.compile('(?:%s)' % regex(k, p, tail))
532 except re.error:
532 except re.error:
533 if src:
533 if src:
534 raise Abort("%s: invalid pattern (%s): %s" %
534 raise Abort("%s: invalid pattern (%s): %s" %
535 (src, k, p))
535 (src, k, p))
536 else:
536 else:
537 raise Abort("invalid pattern (%s): %s" % (k, p))
537 raise Abort("invalid pattern (%s): %s" % (k, p))
538 raise Abort("invalid pattern")
538 raise Abort("invalid pattern")
539
539
540 def globprefix(pat):
540 def globprefix(pat):
541 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
541 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
542 root = []
542 root = []
543 for p in pat.split('/'):
543 for p in pat.split('/'):
544 if contains_glob(p): break
544 if contains_glob(p): break
545 root.append(p)
545 root.append(p)
546 return '/'.join(root) or '.'
546 return '/'.join(root) or '.'
547
547
548 def normalizepats(names, default):
548 def normalizepats(names, default):
549 pats = []
549 pats = []
550 roots = []
550 roots = []
551 anypats = False
551 anypats = False
552 for kind, name in [patkind(p, default) for p in names]:
552 for kind, name in [patkind(p, default) for p in names]:
553 if kind in ('glob', 'relpath'):
553 if kind in ('glob', 'relpath'):
554 name = canonpath(canonroot, cwd, name)
554 name = canonpath(canonroot, cwd, name)
555 elif kind in ('relglob', 'path'):
555 elif kind in ('relglob', 'path'):
556 name = normpath(name)
556 name = normpath(name)
557
557
558 pats.append((kind, name))
558 pats.append((kind, name))
559
559
560 if kind in ('glob', 're', 'relglob', 'relre'):
560 if kind in ('glob', 're', 'relglob', 'relre'):
561 anypats = True
561 anypats = True
562
562
563 if kind == 'glob':
563 if kind == 'glob':
564 root = globprefix(name)
564 root = globprefix(name)
565 roots.append(root)
565 roots.append(root)
566 elif kind in ('relpath', 'path'):
566 elif kind in ('relpath', 'path'):
567 roots.append(name or '.')
567 roots.append(name or '.')
568 elif kind == 'relglob':
568 elif kind == 'relglob':
569 roots.append('.')
569 roots.append('.')
570 return roots, pats, anypats
570 return roots, pats, anypats
571
571
572 roots, pats, anypats = normalizepats(names, dflt_pat)
572 roots, pats, anypats = normalizepats(names, dflt_pat)
573
573
574 patmatch = matchfn(pats, '$') or always
574 patmatch = matchfn(pats, '$') or always
575 incmatch = always
575 incmatch = always
576 if inc:
576 if inc:
577 dummy, inckinds, dummy = normalizepats(inc, 'glob')
577 dummy, inckinds, dummy = normalizepats(inc, 'glob')
578 incmatch = matchfn(inckinds, '(?:/|$)')
578 incmatch = matchfn(inckinds, '(?:/|$)')
579 excmatch = lambda fn: False
579 excmatch = lambda fn: False
580 if exc:
580 if exc:
581 dummy, exckinds, dummy = normalizepats(exc, 'glob')
581 dummy, exckinds, dummy = normalizepats(exc, 'glob')
582 excmatch = matchfn(exckinds, '(?:/|$)')
582 excmatch = matchfn(exckinds, '(?:/|$)')
583
583
584 if not names and inc and not exc:
584 if not names and inc and not exc:
585 # common case: hgignore patterns
585 # common case: hgignore patterns
586 match = incmatch
586 match = incmatch
587 else:
587 else:
588 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
588 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
589
589
590 return (roots, match, (inc or exc or anypats) and True)
590 return (roots, match, (inc or exc or anypats) and True)
591
591
592 _hgexecutable = None
592 _hgexecutable = None
593
593
594 def main_is_frozen():
594 def main_is_frozen():
595 """return True if we are a frozen executable.
595 """return True if we are a frozen executable.
596
596
597 The code supports py2exe (most common, Windows only) and tools/freeze
597 The code supports py2exe (most common, Windows only) and tools/freeze
598 (portable, not much used).
598 (portable, not much used).
599 """
599 """
600 return (hasattr(sys, "frozen") or # new py2exe
600 return (hasattr(sys, "frozen") or # new py2exe
601 hasattr(sys, "importers") or # old py2exe
601 hasattr(sys, "importers") or # old py2exe
602 imp.is_frozen("__main__")) # tools/freeze
602 imp.is_frozen("__main__")) # tools/freeze
603
603
604 def hgexecutable():
604 def hgexecutable():
605 """return location of the 'hg' executable.
605 """return location of the 'hg' executable.
606
606
607 Defaults to $HG or 'hg' in the search path.
607 Defaults to $HG or 'hg' in the search path.
608 """
608 """
609 if _hgexecutable is None:
609 if _hgexecutable is None:
610 hg = os.environ.get('HG')
610 hg = os.environ.get('HG')
611 if hg:
611 if hg:
612 set_hgexecutable(hg)
612 set_hgexecutable(hg)
613 elif main_is_frozen():
613 elif main_is_frozen():
614 set_hgexecutable(sys.executable)
614 set_hgexecutable(sys.executable)
615 else:
615 else:
616 set_hgexecutable(find_exe('hg', 'hg'))
616 set_hgexecutable(find_exe('hg', 'hg'))
617 return _hgexecutable
617 return _hgexecutable
618
618
619 def set_hgexecutable(path):
619 def set_hgexecutable(path):
620 """set location of the 'hg' executable"""
620 """set location of the 'hg' executable"""
621 global _hgexecutable
621 global _hgexecutable
622 _hgexecutable = path
622 _hgexecutable = path
623
623
624 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
624 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
625 '''enhanced shell command execution.
625 '''enhanced shell command execution.
626 run with environment maybe modified, maybe in different dir.
626 run with environment maybe modified, maybe in different dir.
627
627
628 if command fails and onerr is None, return status. if ui object,
628 if command fails and onerr is None, return status. if ui object,
629 print error message and return status, else raise onerr object as
629 print error message and return status, else raise onerr object as
630 exception.'''
630 exception.'''
631 def py2shell(val):
631 def py2shell(val):
632 'convert python object into string that is useful to shell'
632 'convert python object into string that is useful to shell'
633 if val in (None, False):
633 if val in (None, False):
634 return '0'
634 return '0'
635 if val == True:
635 if val == True:
636 return '1'
636 return '1'
637 return str(val)
637 return str(val)
638 oldenv = {}
638 oldenv = {}
639 for k in environ:
639 for k in environ:
640 oldenv[k] = os.environ.get(k)
640 oldenv[k] = os.environ.get(k)
641 if cwd is not None:
641 if cwd is not None:
642 oldcwd = os.getcwd()
642 oldcwd = os.getcwd()
643 origcmd = cmd
643 origcmd = cmd
644 if os.name == 'nt':
644 if os.name == 'nt':
645 cmd = '"%s"' % cmd
645 cmd = '"%s"' % cmd
646 try:
646 try:
647 for k, v in environ.iteritems():
647 for k, v in environ.iteritems():
648 os.environ[k] = py2shell(v)
648 os.environ[k] = py2shell(v)
649 os.environ['HG'] = hgexecutable()
649 os.environ['HG'] = hgexecutable()
650 if cwd is not None and oldcwd != cwd:
650 if cwd is not None and oldcwd != cwd:
651 os.chdir(cwd)
651 os.chdir(cwd)
652 rc = os.system(cmd)
652 rc = os.system(cmd)
653 if sys.platform == 'OpenVMS' and rc & 1:
653 if sys.platform == 'OpenVMS' and rc & 1:
654 rc = 0
654 rc = 0
655 if rc and onerr:
655 if rc and onerr:
656 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
656 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
657 explain_exit(rc)[0])
657 explain_exit(rc)[0])
658 if errprefix:
658 if errprefix:
659 errmsg = '%s: %s' % (errprefix, errmsg)
659 errmsg = '%s: %s' % (errprefix, errmsg)
660 try:
660 try:
661 onerr.warn(errmsg + '\n')
661 onerr.warn(errmsg + '\n')
662 except AttributeError:
662 except AttributeError:
663 raise onerr(errmsg)
663 raise onerr(errmsg)
664 return rc
664 return rc
665 finally:
665 finally:
666 for k, v in oldenv.iteritems():
666 for k, v in oldenv.iteritems():
667 if v is None:
667 if v is None:
668 del os.environ[k]
668 del os.environ[k]
669 else:
669 else:
670 os.environ[k] = v
670 os.environ[k] = v
671 if cwd is not None and oldcwd != cwd:
671 if cwd is not None and oldcwd != cwd:
672 os.chdir(oldcwd)
672 os.chdir(oldcwd)
673
673
674 class SignatureError:
675 pass
676
677 def checksignature(func):
678 '''wrap a function with code to check for calling errors'''
679 def check(*args, **kwargs):
680 try:
681 return func(*args, **kwargs)
682 except TypeError:
683 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
684 raise SignatureError
685 raise
686
687 return check
688
674 # os.path.lexists is not available on python2.3
689 # os.path.lexists is not available on python2.3
675 def lexists(filename):
690 def lexists(filename):
676 "test whether a file with this name exists. does not follow symlinks"
691 "test whether a file with this name exists. does not follow symlinks"
677 try:
692 try:
678 os.lstat(filename)
693 os.lstat(filename)
679 except:
694 except:
680 return False
695 return False
681 return True
696 return True
682
697
683 def rename(src, dst):
698 def rename(src, dst):
684 """forcibly rename a file"""
699 """forcibly rename a file"""
685 try:
700 try:
686 os.rename(src, dst)
701 os.rename(src, dst)
687 except OSError, err: # FIXME: check err (EEXIST ?)
702 except OSError, err: # FIXME: check err (EEXIST ?)
688 # on windows, rename to existing file is not allowed, so we
703 # on windows, rename to existing file is not allowed, so we
689 # must delete destination first. but if file is open, unlink
704 # must delete destination first. but if file is open, unlink
690 # schedules it for delete but does not delete it. rename
705 # schedules it for delete but does not delete it. rename
691 # happens immediately even for open files, so we create
706 # happens immediately even for open files, so we create
692 # temporary file, delete it, rename destination to that name,
707 # temporary file, delete it, rename destination to that name,
693 # then delete that. then rename is safe to do.
708 # then delete that. then rename is safe to do.
694 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
709 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
695 os.close(fd)
710 os.close(fd)
696 os.unlink(temp)
711 os.unlink(temp)
697 os.rename(dst, temp)
712 os.rename(dst, temp)
698 os.unlink(temp)
713 os.unlink(temp)
699 os.rename(src, dst)
714 os.rename(src, dst)
700
715
701 def unlink(f):
716 def unlink(f):
702 """unlink and remove the directory if it is empty"""
717 """unlink and remove the directory if it is empty"""
703 os.unlink(f)
718 os.unlink(f)
704 # try removing directories that might now be empty
719 # try removing directories that might now be empty
705 try:
720 try:
706 os.removedirs(os.path.dirname(f))
721 os.removedirs(os.path.dirname(f))
707 except OSError:
722 except OSError:
708 pass
723 pass
709
724
710 def copyfile(src, dest):
725 def copyfile(src, dest):
711 "copy a file, preserving mode"
726 "copy a file, preserving mode"
712 if os.path.islink(src):
727 if os.path.islink(src):
713 try:
728 try:
714 os.unlink(dest)
729 os.unlink(dest)
715 except:
730 except:
716 pass
731 pass
717 os.symlink(os.readlink(src), dest)
732 os.symlink(os.readlink(src), dest)
718 else:
733 else:
719 try:
734 try:
720 shutil.copyfile(src, dest)
735 shutil.copyfile(src, dest)
721 shutil.copymode(src, dest)
736 shutil.copymode(src, dest)
722 except shutil.Error, inst:
737 except shutil.Error, inst:
723 raise Abort(str(inst))
738 raise Abort(str(inst))
724
739
725 def copyfiles(src, dst, hardlink=None):
740 def copyfiles(src, dst, hardlink=None):
726 """Copy a directory tree using hardlinks if possible"""
741 """Copy a directory tree using hardlinks if possible"""
727
742
728 if hardlink is None:
743 if hardlink is None:
729 hardlink = (os.stat(src).st_dev ==
744 hardlink = (os.stat(src).st_dev ==
730 os.stat(os.path.dirname(dst)).st_dev)
745 os.stat(os.path.dirname(dst)).st_dev)
731
746
732 if os.path.isdir(src):
747 if os.path.isdir(src):
733 os.mkdir(dst)
748 os.mkdir(dst)
734 for name, kind in osutil.listdir(src):
749 for name, kind in osutil.listdir(src):
735 srcname = os.path.join(src, name)
750 srcname = os.path.join(src, name)
736 dstname = os.path.join(dst, name)
751 dstname = os.path.join(dst, name)
737 copyfiles(srcname, dstname, hardlink)
752 copyfiles(srcname, dstname, hardlink)
738 else:
753 else:
739 if hardlink:
754 if hardlink:
740 try:
755 try:
741 os_link(src, dst)
756 os_link(src, dst)
742 except (IOError, OSError):
757 except (IOError, OSError):
743 hardlink = False
758 hardlink = False
744 shutil.copy(src, dst)
759 shutil.copy(src, dst)
745 else:
760 else:
746 shutil.copy(src, dst)
761 shutil.copy(src, dst)
747
762
748 class path_auditor(object):
763 class path_auditor(object):
749 '''ensure that a filesystem path contains no banned components.
764 '''ensure that a filesystem path contains no banned components.
750 the following properties of a path are checked:
765 the following properties of a path are checked:
751
766
752 - under top-level .hg
767 - under top-level .hg
753 - starts at the root of a windows drive
768 - starts at the root of a windows drive
754 - contains ".."
769 - contains ".."
755 - traverses a symlink (e.g. a/symlink_here/b)
770 - traverses a symlink (e.g. a/symlink_here/b)
756 - inside a nested repository'''
771 - inside a nested repository'''
757
772
758 def __init__(self, root):
773 def __init__(self, root):
759 self.audited = set()
774 self.audited = set()
760 self.auditeddir = set()
775 self.auditeddir = set()
761 self.root = root
776 self.root = root
762
777
763 def __call__(self, path):
778 def __call__(self, path):
764 if path in self.audited:
779 if path in self.audited:
765 return
780 return
766 normpath = os.path.normcase(path)
781 normpath = os.path.normcase(path)
767 parts = splitpath(normpath)
782 parts = splitpath(normpath)
768 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
783 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
769 or os.pardir in parts):
784 or os.pardir in parts):
770 raise Abort(_("path contains illegal component: %s") % path)
785 raise Abort(_("path contains illegal component: %s") % path)
771 def check(prefix):
786 def check(prefix):
772 curpath = os.path.join(self.root, prefix)
787 curpath = os.path.join(self.root, prefix)
773 try:
788 try:
774 st = os.lstat(curpath)
789 st = os.lstat(curpath)
775 except OSError, err:
790 except OSError, err:
776 # EINVAL can be raised as invalid path syntax under win32.
791 # EINVAL can be raised as invalid path syntax under win32.
777 # They must be ignored for patterns can be checked too.
792 # They must be ignored for patterns can be checked too.
778 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
793 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
779 raise
794 raise
780 else:
795 else:
781 if stat.S_ISLNK(st.st_mode):
796 if stat.S_ISLNK(st.st_mode):
782 raise Abort(_('path %r traverses symbolic link %r') %
797 raise Abort(_('path %r traverses symbolic link %r') %
783 (path, prefix))
798 (path, prefix))
784 elif (stat.S_ISDIR(st.st_mode) and
799 elif (stat.S_ISDIR(st.st_mode) and
785 os.path.isdir(os.path.join(curpath, '.hg'))):
800 os.path.isdir(os.path.join(curpath, '.hg'))):
786 raise Abort(_('path %r is inside repo %r') %
801 raise Abort(_('path %r is inside repo %r') %
787 (path, prefix))
802 (path, prefix))
788 parts.pop()
803 parts.pop()
789 prefixes = []
804 prefixes = []
790 for n in range(len(parts)):
805 for n in range(len(parts)):
791 prefix = os.sep.join(parts)
806 prefix = os.sep.join(parts)
792 if prefix in self.auditeddir:
807 if prefix in self.auditeddir:
793 break
808 break
794 check(prefix)
809 check(prefix)
795 prefixes.append(prefix)
810 prefixes.append(prefix)
796 parts.pop()
811 parts.pop()
797
812
798 self.audited.add(path)
813 self.audited.add(path)
799 # only add prefixes to the cache after checking everything: we don't
814 # only add prefixes to the cache after checking everything: we don't
800 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
815 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
801 self.auditeddir.update(prefixes)
816 self.auditeddir.update(prefixes)
802
817
803 def _makelock_file(info, pathname):
818 def _makelock_file(info, pathname):
804 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
819 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
805 os.write(ld, info)
820 os.write(ld, info)
806 os.close(ld)
821 os.close(ld)
807
822
808 def _readlock_file(pathname):
823 def _readlock_file(pathname):
809 return posixfile(pathname).read()
824 return posixfile(pathname).read()
810
825
811 def nlinks(pathname):
826 def nlinks(pathname):
812 """Return number of hardlinks for the given file."""
827 """Return number of hardlinks for the given file."""
813 return os.lstat(pathname).st_nlink
828 return os.lstat(pathname).st_nlink
814
829
815 if hasattr(os, 'link'):
830 if hasattr(os, 'link'):
816 os_link = os.link
831 os_link = os.link
817 else:
832 else:
818 def os_link(src, dst):
833 def os_link(src, dst):
819 raise OSError(0, _("Hardlinks not supported"))
834 raise OSError(0, _("Hardlinks not supported"))
820
835
821 def fstat(fp):
836 def fstat(fp):
822 '''stat file object that may not have fileno method.'''
837 '''stat file object that may not have fileno method.'''
823 try:
838 try:
824 return os.fstat(fp.fileno())
839 return os.fstat(fp.fileno())
825 except AttributeError:
840 except AttributeError:
826 return os.stat(fp.name)
841 return os.stat(fp.name)
827
842
828 posixfile = file
843 posixfile = file
829
844
830 def openhardlinks():
845 def openhardlinks():
831 '''return true if it is safe to hold open file handles to hardlinks'''
846 '''return true if it is safe to hold open file handles to hardlinks'''
832 return True
847 return True
833
848
834 def _statfiles(files):
849 def _statfiles(files):
835 'Stat each file in files and yield stat or None if file does not exist.'
850 'Stat each file in files and yield stat or None if file does not exist.'
836 lstat = os.lstat
851 lstat = os.lstat
837 for nf in files:
852 for nf in files:
838 try:
853 try:
839 st = lstat(nf)
854 st = lstat(nf)
840 except OSError, err:
855 except OSError, err:
841 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
856 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
842 raise
857 raise
843 st = None
858 st = None
844 yield st
859 yield st
845
860
846 def _statfiles_clustered(files):
861 def _statfiles_clustered(files):
847 '''Stat each file in files and yield stat or None if file does not exist.
862 '''Stat each file in files and yield stat or None if file does not exist.
848 Cluster and cache stat per directory to minimize number of OS stat calls.'''
863 Cluster and cache stat per directory to minimize number of OS stat calls.'''
849 lstat = os.lstat
864 lstat = os.lstat
850 ncase = os.path.normcase
865 ncase = os.path.normcase
851 sep = os.sep
866 sep = os.sep
852 dircache = {} # dirname -> filename -> status | None if file does not exist
867 dircache = {} # dirname -> filename -> status | None if file does not exist
853 for nf in files:
868 for nf in files:
854 nf = ncase(nf)
869 nf = ncase(nf)
855 pos = nf.rfind(sep)
870 pos = nf.rfind(sep)
856 if pos == -1:
871 if pos == -1:
857 dir, base = '.', nf
872 dir, base = '.', nf
858 else:
873 else:
859 dir, base = nf[:pos+1], nf[pos+1:]
874 dir, base = nf[:pos+1], nf[pos+1:]
860 cache = dircache.get(dir, None)
875 cache = dircache.get(dir, None)
861 if cache is None:
876 if cache is None:
862 try:
877 try:
863 dmap = dict([(ncase(n), s)
878 dmap = dict([(ncase(n), s)
864 for n, k, s in osutil.listdir(dir, True)])
879 for n, k, s in osutil.listdir(dir, True)])
865 except OSError, err:
880 except OSError, err:
866 # handle directory not found in Python version prior to 2.5
881 # handle directory not found in Python version prior to 2.5
867 # Python <= 2.4 returns native Windows code 3 in errno
882 # Python <= 2.4 returns native Windows code 3 in errno
868 # Python >= 2.5 returns ENOENT and adds winerror field
883 # Python >= 2.5 returns ENOENT and adds winerror field
869 # EINVAL is raised if dir is not a directory.
884 # EINVAL is raised if dir is not a directory.
870 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
885 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
871 errno.ENOTDIR):
886 errno.ENOTDIR):
872 raise
887 raise
873 dmap = {}
888 dmap = {}
874 cache = dircache.setdefault(dir, dmap)
889 cache = dircache.setdefault(dir, dmap)
875 yield cache.get(base, None)
890 yield cache.get(base, None)
876
891
877 if sys.platform == 'win32':
892 if sys.platform == 'win32':
878 statfiles = _statfiles_clustered
893 statfiles = _statfiles_clustered
879 else:
894 else:
880 statfiles = _statfiles
895 statfiles = _statfiles
881
896
882 getuser_fallback = None
897 getuser_fallback = None
883
898
884 def getuser():
899 def getuser():
885 '''return name of current user'''
900 '''return name of current user'''
886 try:
901 try:
887 return getpass.getuser()
902 return getpass.getuser()
888 except ImportError:
903 except ImportError:
889 # import of pwd will fail on windows - try fallback
904 # import of pwd will fail on windows - try fallback
890 if getuser_fallback:
905 if getuser_fallback:
891 return getuser_fallback()
906 return getuser_fallback()
892 # raised if win32api not available
907 # raised if win32api not available
893 raise Abort(_('user name not available - set USERNAME '
908 raise Abort(_('user name not available - set USERNAME '
894 'environment variable'))
909 'environment variable'))
895
910
896 def username(uid=None):
911 def username(uid=None):
897 """Return the name of the user with the given uid.
912 """Return the name of the user with the given uid.
898
913
899 If uid is None, return the name of the current user."""
914 If uid is None, return the name of the current user."""
900 try:
915 try:
901 import pwd
916 import pwd
902 if uid is None:
917 if uid is None:
903 uid = os.getuid()
918 uid = os.getuid()
904 try:
919 try:
905 return pwd.getpwuid(uid)[0]
920 return pwd.getpwuid(uid)[0]
906 except KeyError:
921 except KeyError:
907 return str(uid)
922 return str(uid)
908 except ImportError:
923 except ImportError:
909 return None
924 return None
910
925
911 def groupname(gid=None):
926 def groupname(gid=None):
912 """Return the name of the group with the given gid.
927 """Return the name of the group with the given gid.
913
928
914 If gid is None, return the name of the current group."""
929 If gid is None, return the name of the current group."""
915 try:
930 try:
916 import grp
931 import grp
917 if gid is None:
932 if gid is None:
918 gid = os.getgid()
933 gid = os.getgid()
919 try:
934 try:
920 return grp.getgrgid(gid)[0]
935 return grp.getgrgid(gid)[0]
921 except KeyError:
936 except KeyError:
922 return str(gid)
937 return str(gid)
923 except ImportError:
938 except ImportError:
924 return None
939 return None
925
940
926 # File system features
941 # File system features
927
942
928 def checkcase(path):
943 def checkcase(path):
929 """
944 """
930 Check whether the given path is on a case-sensitive filesystem
945 Check whether the given path is on a case-sensitive filesystem
931
946
932 Requires a path (like /foo/.hg) ending with a foldable final
947 Requires a path (like /foo/.hg) ending with a foldable final
933 directory component.
948 directory component.
934 """
949 """
935 s1 = os.stat(path)
950 s1 = os.stat(path)
936 d, b = os.path.split(path)
951 d, b = os.path.split(path)
937 p2 = os.path.join(d, b.upper())
952 p2 = os.path.join(d, b.upper())
938 if path == p2:
953 if path == p2:
939 p2 = os.path.join(d, b.lower())
954 p2 = os.path.join(d, b.lower())
940 try:
955 try:
941 s2 = os.stat(p2)
956 s2 = os.stat(p2)
942 if s2 == s1:
957 if s2 == s1:
943 return False
958 return False
944 return True
959 return True
945 except:
960 except:
946 return True
961 return True
947
962
948 _fspathcache = {}
963 _fspathcache = {}
949 def fspath(name, root):
964 def fspath(name, root):
950 '''Get name in the case stored in the filesystem
965 '''Get name in the case stored in the filesystem
951
966
952 The name is either relative to root, or it is an absolute path starting
967 The name is either relative to root, or it is an absolute path starting
953 with root. Note that this function is unnecessary, and should not be
968 with root. Note that this function is unnecessary, and should not be
954 called, for case-sensitive filesystems (simply because it's expensive).
969 called, for case-sensitive filesystems (simply because it's expensive).
955 '''
970 '''
956 # If name is absolute, make it relative
971 # If name is absolute, make it relative
957 if name.lower().startswith(root.lower()):
972 if name.lower().startswith(root.lower()):
958 l = len(root)
973 l = len(root)
959 if name[l] == os.sep or name[l] == os.altsep:
974 if name[l] == os.sep or name[l] == os.altsep:
960 l = l + 1
975 l = l + 1
961 name = name[l:]
976 name = name[l:]
962
977
963 if not os.path.exists(os.path.join(root, name)):
978 if not os.path.exists(os.path.join(root, name)):
964 return None
979 return None
965
980
966 seps = os.sep
981 seps = os.sep
967 if os.altsep:
982 if os.altsep:
968 seps = seps + os.altsep
983 seps = seps + os.altsep
969 # Protect backslashes. This gets silly very quickly.
984 # Protect backslashes. This gets silly very quickly.
970 seps.replace('\\','\\\\')
985 seps.replace('\\','\\\\')
971 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
986 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
972 dir = os.path.normcase(os.path.normpath(root))
987 dir = os.path.normcase(os.path.normpath(root))
973 result = []
988 result = []
974 for part, sep in pattern.findall(name):
989 for part, sep in pattern.findall(name):
975 if sep:
990 if sep:
976 result.append(sep)
991 result.append(sep)
977 continue
992 continue
978
993
979 if dir not in _fspathcache:
994 if dir not in _fspathcache:
980 _fspathcache[dir] = os.listdir(dir)
995 _fspathcache[dir] = os.listdir(dir)
981 contents = _fspathcache[dir]
996 contents = _fspathcache[dir]
982
997
983 lpart = part.lower()
998 lpart = part.lower()
984 for n in contents:
999 for n in contents:
985 if n.lower() == lpart:
1000 if n.lower() == lpart:
986 result.append(n)
1001 result.append(n)
987 break
1002 break
988 else:
1003 else:
989 # Cannot happen, as the file exists!
1004 # Cannot happen, as the file exists!
990 result.append(part)
1005 result.append(part)
991 dir = os.path.join(dir, lpart)
1006 dir = os.path.join(dir, lpart)
992
1007
993 return ''.join(result)
1008 return ''.join(result)
994
1009
995 def checkexec(path):
1010 def checkexec(path):
996 """
1011 """
997 Check whether the given path is on a filesystem with UNIX-like exec flags
1012 Check whether the given path is on a filesystem with UNIX-like exec flags
998
1013
999 Requires a directory (like /foo/.hg)
1014 Requires a directory (like /foo/.hg)
1000 """
1015 """
1001
1016
1002 # VFAT on some Linux versions can flip mode but it doesn't persist
1017 # VFAT on some Linux versions can flip mode but it doesn't persist
1003 # a FS remount. Frequently we can detect it if files are created
1018 # a FS remount. Frequently we can detect it if files are created
1004 # with exec bit on.
1019 # with exec bit on.
1005
1020
1006 try:
1021 try:
1007 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1022 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1008 fh, fn = tempfile.mkstemp("", "", path)
1023 fh, fn = tempfile.mkstemp("", "", path)
1009 try:
1024 try:
1010 os.close(fh)
1025 os.close(fh)
1011 m = os.stat(fn).st_mode & 0777
1026 m = os.stat(fn).st_mode & 0777
1012 new_file_has_exec = m & EXECFLAGS
1027 new_file_has_exec = m & EXECFLAGS
1013 os.chmod(fn, m ^ EXECFLAGS)
1028 os.chmod(fn, m ^ EXECFLAGS)
1014 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1029 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1015 finally:
1030 finally:
1016 os.unlink(fn)
1031 os.unlink(fn)
1017 except (IOError, OSError):
1032 except (IOError, OSError):
1018 # we don't care, the user probably won't be able to commit anyway
1033 # we don't care, the user probably won't be able to commit anyway
1019 return False
1034 return False
1020 return not (new_file_has_exec or exec_flags_cannot_flip)
1035 return not (new_file_has_exec or exec_flags_cannot_flip)
1021
1036
1022 def checklink(path):
1037 def checklink(path):
1023 """check whether the given path is on a symlink-capable filesystem"""
1038 """check whether the given path is on a symlink-capable filesystem"""
1024 # mktemp is not racy because symlink creation will fail if the
1039 # mktemp is not racy because symlink creation will fail if the
1025 # file already exists
1040 # file already exists
1026 name = tempfile.mktemp(dir=path)
1041 name = tempfile.mktemp(dir=path)
1027 try:
1042 try:
1028 os.symlink(".", name)
1043 os.symlink(".", name)
1029 os.unlink(name)
1044 os.unlink(name)
1030 return True
1045 return True
1031 except (OSError, AttributeError):
1046 except (OSError, AttributeError):
1032 return False
1047 return False
1033
1048
1034 _umask = os.umask(0)
1049 _umask = os.umask(0)
1035 os.umask(_umask)
1050 os.umask(_umask)
1036
1051
1037 def needbinarypatch():
1052 def needbinarypatch():
1038 """return True if patches should be applied in binary mode by default."""
1053 """return True if patches should be applied in binary mode by default."""
1039 return os.name == 'nt'
1054 return os.name == 'nt'
1040
1055
1041 def endswithsep(path):
1056 def endswithsep(path):
1042 '''Check path ends with os.sep or os.altsep.'''
1057 '''Check path ends with os.sep or os.altsep.'''
1043 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1058 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1044
1059
1045 def splitpath(path):
1060 def splitpath(path):
1046 '''Split path by os.sep.
1061 '''Split path by os.sep.
1047 Note that this function does not use os.altsep because this is
1062 Note that this function does not use os.altsep because this is
1048 an alternative of simple "xxx.split(os.sep)".
1063 an alternative of simple "xxx.split(os.sep)".
1049 It is recommended to use os.path.normpath() before using this
1064 It is recommended to use os.path.normpath() before using this
1050 function if need.'''
1065 function if need.'''
1051 return path.split(os.sep)
1066 return path.split(os.sep)
1052
1067
1053 def gui():
1068 def gui():
1054 '''Are we running in a GUI?'''
1069 '''Are we running in a GUI?'''
1055 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1070 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1056
1071
1057 def lookup_reg(key, name=None, scope=None):
1072 def lookup_reg(key, name=None, scope=None):
1058 return None
1073 return None
1059
1074
1060 # Platform specific variants
1075 # Platform specific variants
1061 if os.name == 'nt':
1076 if os.name == 'nt':
1062 import msvcrt
1077 import msvcrt
1063 nulldev = 'NUL:'
1078 nulldev = 'NUL:'
1064
1079
1065 class winstdout:
1080 class winstdout:
1066 '''stdout on windows misbehaves if sent through a pipe'''
1081 '''stdout on windows misbehaves if sent through a pipe'''
1067
1082
1068 def __init__(self, fp):
1083 def __init__(self, fp):
1069 self.fp = fp
1084 self.fp = fp
1070
1085
1071 def __getattr__(self, key):
1086 def __getattr__(self, key):
1072 return getattr(self.fp, key)
1087 return getattr(self.fp, key)
1073
1088
1074 def close(self):
1089 def close(self):
1075 try:
1090 try:
1076 self.fp.close()
1091 self.fp.close()
1077 except: pass
1092 except: pass
1078
1093
1079 def write(self, s):
1094 def write(self, s):
1080 try:
1095 try:
1081 # This is workaround for "Not enough space" error on
1096 # This is workaround for "Not enough space" error on
1082 # writing large size of data to console.
1097 # writing large size of data to console.
1083 limit = 16000
1098 limit = 16000
1084 l = len(s)
1099 l = len(s)
1085 start = 0
1100 start = 0
1086 while start < l:
1101 while start < l:
1087 end = start + limit
1102 end = start + limit
1088 self.fp.write(s[start:end])
1103 self.fp.write(s[start:end])
1089 start = end
1104 start = end
1090 except IOError, inst:
1105 except IOError, inst:
1091 if inst.errno != 0: raise
1106 if inst.errno != 0: raise
1092 self.close()
1107 self.close()
1093 raise IOError(errno.EPIPE, 'Broken pipe')
1108 raise IOError(errno.EPIPE, 'Broken pipe')
1094
1109
1095 def flush(self):
1110 def flush(self):
1096 try:
1111 try:
1097 return self.fp.flush()
1112 return self.fp.flush()
1098 except IOError, inst:
1113 except IOError, inst:
1099 if inst.errno != errno.EINVAL: raise
1114 if inst.errno != errno.EINVAL: raise
1100 self.close()
1115 self.close()
1101 raise IOError(errno.EPIPE, 'Broken pipe')
1116 raise IOError(errno.EPIPE, 'Broken pipe')
1102
1117
1103 sys.stdout = winstdout(sys.stdout)
1118 sys.stdout = winstdout(sys.stdout)
1104
1119
1105 def _is_win_9x():
1120 def _is_win_9x():
1106 '''return true if run on windows 95, 98 or me.'''
1121 '''return true if run on windows 95, 98 or me.'''
1107 try:
1122 try:
1108 return sys.getwindowsversion()[3] == 1
1123 return sys.getwindowsversion()[3] == 1
1109 except AttributeError:
1124 except AttributeError:
1110 return 'command' in os.environ.get('comspec', '')
1125 return 'command' in os.environ.get('comspec', '')
1111
1126
1112 def openhardlinks():
1127 def openhardlinks():
1113 return not _is_win_9x and "win32api" in locals()
1128 return not _is_win_9x and "win32api" in locals()
1114
1129
1115 def system_rcpath():
1130 def system_rcpath():
1116 try:
1131 try:
1117 return system_rcpath_win32()
1132 return system_rcpath_win32()
1118 except:
1133 except:
1119 return [r'c:\mercurial\mercurial.ini']
1134 return [r'c:\mercurial\mercurial.ini']
1120
1135
1121 def user_rcpath():
1136 def user_rcpath():
1122 '''return os-specific hgrc search path to the user dir'''
1137 '''return os-specific hgrc search path to the user dir'''
1123 try:
1138 try:
1124 path = user_rcpath_win32()
1139 path = user_rcpath_win32()
1125 except:
1140 except:
1126 home = os.path.expanduser('~')
1141 home = os.path.expanduser('~')
1127 path = [os.path.join(home, 'mercurial.ini'),
1142 path = [os.path.join(home, 'mercurial.ini'),
1128 os.path.join(home, '.hgrc')]
1143 os.path.join(home, '.hgrc')]
1129 userprofile = os.environ.get('USERPROFILE')
1144 userprofile = os.environ.get('USERPROFILE')
1130 if userprofile:
1145 if userprofile:
1131 path.append(os.path.join(userprofile, 'mercurial.ini'))
1146 path.append(os.path.join(userprofile, 'mercurial.ini'))
1132 path.append(os.path.join(userprofile, '.hgrc'))
1147 path.append(os.path.join(userprofile, '.hgrc'))
1133 return path
1148 return path
1134
1149
1135 def parse_patch_output(output_line):
1150 def parse_patch_output(output_line):
1136 """parses the output produced by patch and returns the file name"""
1151 """parses the output produced by patch and returns the file name"""
1137 pf = output_line[14:]
1152 pf = output_line[14:]
1138 if pf[0] == '`':
1153 if pf[0] == '`':
1139 pf = pf[1:-1] # Remove the quotes
1154 pf = pf[1:-1] # Remove the quotes
1140 return pf
1155 return pf
1141
1156
1142 def sshargs(sshcmd, host, user, port):
1157 def sshargs(sshcmd, host, user, port):
1143 '''Build argument list for ssh or Plink'''
1158 '''Build argument list for ssh or Plink'''
1144 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1159 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1145 args = user and ("%s@%s" % (user, host)) or host
1160 args = user and ("%s@%s" % (user, host)) or host
1146 return port and ("%s %s %s" % (args, pflag, port)) or args
1161 return port and ("%s %s %s" % (args, pflag, port)) or args
1147
1162
1148 def testpid(pid):
1163 def testpid(pid):
1149 '''return False if pid dead, True if running or not known'''
1164 '''return False if pid dead, True if running or not known'''
1150 return True
1165 return True
1151
1166
1152 def set_flags(f, l, x):
1167 def set_flags(f, l, x):
1153 pass
1168 pass
1154
1169
1155 def set_binary(fd):
1170 def set_binary(fd):
1156 # When run without console, pipes may expose invalid
1171 # When run without console, pipes may expose invalid
1157 # fileno(), usually set to -1.
1172 # fileno(), usually set to -1.
1158 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1173 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1159 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1174 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1160
1175
1161 def pconvert(path):
1176 def pconvert(path):
1162 return '/'.join(splitpath(path))
1177 return '/'.join(splitpath(path))
1163
1178
1164 def localpath(path):
1179 def localpath(path):
1165 return path.replace('/', '\\')
1180 return path.replace('/', '\\')
1166
1181
1167 def normpath(path):
1182 def normpath(path):
1168 return pconvert(os.path.normpath(path))
1183 return pconvert(os.path.normpath(path))
1169
1184
1170 makelock = _makelock_file
1185 makelock = _makelock_file
1171 readlock = _readlock_file
1186 readlock = _readlock_file
1172
1187
1173 def samestat(s1, s2):
1188 def samestat(s1, s2):
1174 return False
1189 return False
1175
1190
1176 # A sequence of backslashes is special iff it precedes a double quote:
1191 # A sequence of backslashes is special iff it precedes a double quote:
1177 # - if there's an even number of backslashes, the double quote is not
1192 # - if there's an even number of backslashes, the double quote is not
1178 # quoted (i.e. it ends the quoted region)
1193 # quoted (i.e. it ends the quoted region)
1179 # - if there's an odd number of backslashes, the double quote is quoted
1194 # - if there's an odd number of backslashes, the double quote is quoted
1180 # - in both cases, every pair of backslashes is unquoted into a single
1195 # - in both cases, every pair of backslashes is unquoted into a single
1181 # backslash
1196 # backslash
1182 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1197 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1183 # So, to quote a string, we must surround it in double quotes, double
1198 # So, to quote a string, we must surround it in double quotes, double
1184 # the number of backslashes that preceed double quotes and add another
1199 # the number of backslashes that preceed double quotes and add another
1185 # backslash before every double quote (being careful with the double
1200 # backslash before every double quote (being careful with the double
1186 # quote we've appended to the end)
1201 # quote we've appended to the end)
1187 _quotere = None
1202 _quotere = None
1188 def shellquote(s):
1203 def shellquote(s):
1189 global _quotere
1204 global _quotere
1190 if _quotere is None:
1205 if _quotere is None:
1191 _quotere = re.compile(r'(\\*)("|\\$)')
1206 _quotere = re.compile(r'(\\*)("|\\$)')
1192 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1207 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1193
1208
1194 def quotecommand(cmd):
1209 def quotecommand(cmd):
1195 """Build a command string suitable for os.popen* calls."""
1210 """Build a command string suitable for os.popen* calls."""
1196 # The extra quotes are needed because popen* runs the command
1211 # The extra quotes are needed because popen* runs the command
1197 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1212 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1198 return '"' + cmd + '"'
1213 return '"' + cmd + '"'
1199
1214
1200 def popen(command, mode='r'):
1215 def popen(command, mode='r'):
1201 # Work around "popen spawned process may not write to stdout
1216 # Work around "popen spawned process may not write to stdout
1202 # under windows"
1217 # under windows"
1203 # http://bugs.python.org/issue1366
1218 # http://bugs.python.org/issue1366
1204 command += " 2> %s" % nulldev
1219 command += " 2> %s" % nulldev
1205 return os.popen(quotecommand(command), mode)
1220 return os.popen(quotecommand(command), mode)
1206
1221
1207 def explain_exit(code):
1222 def explain_exit(code):
1208 return _("exited with status %d") % code, code
1223 return _("exited with status %d") % code, code
1209
1224
1210 # if you change this stub into a real check, please try to implement the
1225 # if you change this stub into a real check, please try to implement the
1211 # username and groupname functions above, too.
1226 # username and groupname functions above, too.
1212 def isowner(fp, st=None):
1227 def isowner(fp, st=None):
1213 return True
1228 return True
1214
1229
1215 def find_in_path(name, path, default=None):
1230 def find_in_path(name, path, default=None):
1216 '''find name in search path. path can be string (will be split
1231 '''find name in search path. path can be string (will be split
1217 with os.pathsep), or iterable thing that returns strings. if name
1232 with os.pathsep), or iterable thing that returns strings. if name
1218 found, return path to name. else return default. name is looked up
1233 found, return path to name. else return default. name is looked up
1219 using cmd.exe rules, using PATHEXT.'''
1234 using cmd.exe rules, using PATHEXT.'''
1220 if isinstance(path, str):
1235 if isinstance(path, str):
1221 path = path.split(os.pathsep)
1236 path = path.split(os.pathsep)
1222
1237
1223 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1238 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1224 pathext = pathext.lower().split(os.pathsep)
1239 pathext = pathext.lower().split(os.pathsep)
1225 isexec = os.path.splitext(name)[1].lower() in pathext
1240 isexec = os.path.splitext(name)[1].lower() in pathext
1226
1241
1227 for p in path:
1242 for p in path:
1228 p_name = os.path.join(p, name)
1243 p_name = os.path.join(p, name)
1229
1244
1230 if isexec and os.path.exists(p_name):
1245 if isexec and os.path.exists(p_name):
1231 return p_name
1246 return p_name
1232
1247
1233 for ext in pathext:
1248 for ext in pathext:
1234 p_name_ext = p_name + ext
1249 p_name_ext = p_name + ext
1235 if os.path.exists(p_name_ext):
1250 if os.path.exists(p_name_ext):
1236 return p_name_ext
1251 return p_name_ext
1237 return default
1252 return default
1238
1253
1239 def set_signal_handler():
1254 def set_signal_handler():
1240 try:
1255 try:
1241 set_signal_handler_win32()
1256 set_signal_handler_win32()
1242 except NameError:
1257 except NameError:
1243 pass
1258 pass
1244
1259
1245 try:
1260 try:
1246 # override functions with win32 versions if possible
1261 # override functions with win32 versions if possible
1247 from util_win32 import *
1262 from util_win32 import *
1248 if not _is_win_9x():
1263 if not _is_win_9x():
1249 posixfile = posixfile_nt
1264 posixfile = posixfile_nt
1250 except ImportError:
1265 except ImportError:
1251 pass
1266 pass
1252
1267
1253 else:
1268 else:
1254 nulldev = '/dev/null'
1269 nulldev = '/dev/null'
1255
1270
1256 def rcfiles(path):
1271 def rcfiles(path):
1257 rcs = [os.path.join(path, 'hgrc')]
1272 rcs = [os.path.join(path, 'hgrc')]
1258 rcdir = os.path.join(path, 'hgrc.d')
1273 rcdir = os.path.join(path, 'hgrc.d')
1259 try:
1274 try:
1260 rcs.extend([os.path.join(rcdir, f)
1275 rcs.extend([os.path.join(rcdir, f)
1261 for f, kind in osutil.listdir(rcdir)
1276 for f, kind in osutil.listdir(rcdir)
1262 if f.endswith(".rc")])
1277 if f.endswith(".rc")])
1263 except OSError:
1278 except OSError:
1264 pass
1279 pass
1265 return rcs
1280 return rcs
1266
1281
1267 def system_rcpath():
1282 def system_rcpath():
1268 path = []
1283 path = []
1269 # old mod_python does not set sys.argv
1284 # old mod_python does not set sys.argv
1270 if len(getattr(sys, 'argv', [])) > 0:
1285 if len(getattr(sys, 'argv', [])) > 0:
1271 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1286 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1272 '/../etc/mercurial'))
1287 '/../etc/mercurial'))
1273 path.extend(rcfiles('/etc/mercurial'))
1288 path.extend(rcfiles('/etc/mercurial'))
1274 return path
1289 return path
1275
1290
1276 def user_rcpath():
1291 def user_rcpath():
1277 return [os.path.expanduser('~/.hgrc')]
1292 return [os.path.expanduser('~/.hgrc')]
1278
1293
1279 def parse_patch_output(output_line):
1294 def parse_patch_output(output_line):
1280 """parses the output produced by patch and returns the file name"""
1295 """parses the output produced by patch and returns the file name"""
1281 pf = output_line[14:]
1296 pf = output_line[14:]
1282 if os.sys.platform == 'OpenVMS':
1297 if os.sys.platform == 'OpenVMS':
1283 if pf[0] == '`':
1298 if pf[0] == '`':
1284 pf = pf[1:-1] # Remove the quotes
1299 pf = pf[1:-1] # Remove the quotes
1285 else:
1300 else:
1286 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1301 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1287 pf = pf[1:-1] # Remove the quotes
1302 pf = pf[1:-1] # Remove the quotes
1288 return pf
1303 return pf
1289
1304
1290 def sshargs(sshcmd, host, user, port):
1305 def sshargs(sshcmd, host, user, port):
1291 '''Build argument list for ssh'''
1306 '''Build argument list for ssh'''
1292 args = user and ("%s@%s" % (user, host)) or host
1307 args = user and ("%s@%s" % (user, host)) or host
1293 return port and ("%s -p %s" % (args, port)) or args
1308 return port and ("%s -p %s" % (args, port)) or args
1294
1309
1295 def is_exec(f):
1310 def is_exec(f):
1296 """check whether a file is executable"""
1311 """check whether a file is executable"""
1297 return (os.lstat(f).st_mode & 0100 != 0)
1312 return (os.lstat(f).st_mode & 0100 != 0)
1298
1313
1299 def set_flags(f, l, x):
1314 def set_flags(f, l, x):
1300 s = os.lstat(f).st_mode
1315 s = os.lstat(f).st_mode
1301 if l:
1316 if l:
1302 if not stat.S_ISLNK(s):
1317 if not stat.S_ISLNK(s):
1303 # switch file to link
1318 # switch file to link
1304 data = file(f).read()
1319 data = file(f).read()
1305 os.unlink(f)
1320 os.unlink(f)
1306 try:
1321 try:
1307 os.symlink(data, f)
1322 os.symlink(data, f)
1308 except:
1323 except:
1309 # failed to make a link, rewrite file
1324 # failed to make a link, rewrite file
1310 file(f, "w").write(data)
1325 file(f, "w").write(data)
1311 # no chmod needed at this point
1326 # no chmod needed at this point
1312 return
1327 return
1313 if stat.S_ISLNK(s):
1328 if stat.S_ISLNK(s):
1314 # switch link to file
1329 # switch link to file
1315 data = os.readlink(f)
1330 data = os.readlink(f)
1316 os.unlink(f)
1331 os.unlink(f)
1317 file(f, "w").write(data)
1332 file(f, "w").write(data)
1318 s = 0666 & ~_umask # avoid restatting for chmod
1333 s = 0666 & ~_umask # avoid restatting for chmod
1319
1334
1320 sx = s & 0100
1335 sx = s & 0100
1321 if x and not sx:
1336 if x and not sx:
1322 # Turn on +x for every +r bit when making a file executable
1337 # Turn on +x for every +r bit when making a file executable
1323 # and obey umask.
1338 # and obey umask.
1324 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1339 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1325 elif not x and sx:
1340 elif not x and sx:
1326 # Turn off all +x bits
1341 # Turn off all +x bits
1327 os.chmod(f, s & 0666)
1342 os.chmod(f, s & 0666)
1328
1343
1329 def set_binary(fd):
1344 def set_binary(fd):
1330 pass
1345 pass
1331
1346
1332 def pconvert(path):
1347 def pconvert(path):
1333 return path
1348 return path
1334
1349
1335 def localpath(path):
1350 def localpath(path):
1336 return path
1351 return path
1337
1352
1338 normpath = os.path.normpath
1353 normpath = os.path.normpath
1339 samestat = os.path.samestat
1354 samestat = os.path.samestat
1340
1355
1341 def makelock(info, pathname):
1356 def makelock(info, pathname):
1342 try:
1357 try:
1343 os.symlink(info, pathname)
1358 os.symlink(info, pathname)
1344 except OSError, why:
1359 except OSError, why:
1345 if why.errno == errno.EEXIST:
1360 if why.errno == errno.EEXIST:
1346 raise
1361 raise
1347 else:
1362 else:
1348 _makelock_file(info, pathname)
1363 _makelock_file(info, pathname)
1349
1364
1350 def readlock(pathname):
1365 def readlock(pathname):
1351 try:
1366 try:
1352 return os.readlink(pathname)
1367 return os.readlink(pathname)
1353 except OSError, why:
1368 except OSError, why:
1354 if why.errno in (errno.EINVAL, errno.ENOSYS):
1369 if why.errno in (errno.EINVAL, errno.ENOSYS):
1355 return _readlock_file(pathname)
1370 return _readlock_file(pathname)
1356 else:
1371 else:
1357 raise
1372 raise
1358
1373
1359 def shellquote(s):
1374 def shellquote(s):
1360 if os.sys.platform == 'OpenVMS':
1375 if os.sys.platform == 'OpenVMS':
1361 return '"%s"' % s
1376 return '"%s"' % s
1362 else:
1377 else:
1363 return "'%s'" % s.replace("'", "'\\''")
1378 return "'%s'" % s.replace("'", "'\\''")
1364
1379
1365 def quotecommand(cmd):
1380 def quotecommand(cmd):
1366 return cmd
1381 return cmd
1367
1382
1368 def popen(command, mode='r'):
1383 def popen(command, mode='r'):
1369 return os.popen(command, mode)
1384 return os.popen(command, mode)
1370
1385
1371 def testpid(pid):
1386 def testpid(pid):
1372 '''return False if pid dead, True if running or not sure'''
1387 '''return False if pid dead, True if running or not sure'''
1373 if os.sys.platform == 'OpenVMS':
1388 if os.sys.platform == 'OpenVMS':
1374 return True
1389 return True
1375 try:
1390 try:
1376 os.kill(pid, 0)
1391 os.kill(pid, 0)
1377 return True
1392 return True
1378 except OSError, inst:
1393 except OSError, inst:
1379 return inst.errno != errno.ESRCH
1394 return inst.errno != errno.ESRCH
1380
1395
1381 def explain_exit(code):
1396 def explain_exit(code):
1382 """return a 2-tuple (desc, code) describing a process's status"""
1397 """return a 2-tuple (desc, code) describing a process's status"""
1383 if os.WIFEXITED(code):
1398 if os.WIFEXITED(code):
1384 val = os.WEXITSTATUS(code)
1399 val = os.WEXITSTATUS(code)
1385 return _("exited with status %d") % val, val
1400 return _("exited with status %d") % val, val
1386 elif os.WIFSIGNALED(code):
1401 elif os.WIFSIGNALED(code):
1387 val = os.WTERMSIG(code)
1402 val = os.WTERMSIG(code)
1388 return _("killed by signal %d") % val, val
1403 return _("killed by signal %d") % val, val
1389 elif os.WIFSTOPPED(code):
1404 elif os.WIFSTOPPED(code):
1390 val = os.WSTOPSIG(code)
1405 val = os.WSTOPSIG(code)
1391 return _("stopped by signal %d") % val, val
1406 return _("stopped by signal %d") % val, val
1392 raise ValueError(_("invalid exit code"))
1407 raise ValueError(_("invalid exit code"))
1393
1408
1394 def isowner(fp, st=None):
1409 def isowner(fp, st=None):
1395 """Return True if the file object f belongs to the current user.
1410 """Return True if the file object f belongs to the current user.
1396
1411
1397 The return value of a util.fstat(f) may be passed as the st argument.
1412 The return value of a util.fstat(f) may be passed as the st argument.
1398 """
1413 """
1399 if st is None:
1414 if st is None:
1400 st = fstat(fp)
1415 st = fstat(fp)
1401 return st.st_uid == os.getuid()
1416 return st.st_uid == os.getuid()
1402
1417
1403 def find_in_path(name, path, default=None):
1418 def find_in_path(name, path, default=None):
1404 '''find name in search path. path can be string (will be split
1419 '''find name in search path. path can be string (will be split
1405 with os.pathsep), or iterable thing that returns strings. if name
1420 with os.pathsep), or iterable thing that returns strings. if name
1406 found, return path to name. else return default.'''
1421 found, return path to name. else return default.'''
1407 if isinstance(path, str):
1422 if isinstance(path, str):
1408 path = path.split(os.pathsep)
1423 path = path.split(os.pathsep)
1409 for p in path:
1424 for p in path:
1410 p_name = os.path.join(p, name)
1425 p_name = os.path.join(p, name)
1411 if os.path.exists(p_name):
1426 if os.path.exists(p_name):
1412 return p_name
1427 return p_name
1413 return default
1428 return default
1414
1429
1415 def set_signal_handler():
1430 def set_signal_handler():
1416 pass
1431 pass
1417
1432
1418 def find_exe(name, default=None):
1433 def find_exe(name, default=None):
1419 '''find path of an executable.
1434 '''find path of an executable.
1420 if name contains a path component, return it as is. otherwise,
1435 if name contains a path component, return it as is. otherwise,
1421 use normal executable search path.'''
1436 use normal executable search path.'''
1422
1437
1423 if os.sep in name or sys.platform == 'OpenVMS':
1438 if os.sep in name or sys.platform == 'OpenVMS':
1424 # don't check the executable bit. if the file isn't
1439 # don't check the executable bit. if the file isn't
1425 # executable, whoever tries to actually run it will give a
1440 # executable, whoever tries to actually run it will give a
1426 # much more useful error message.
1441 # much more useful error message.
1427 return name
1442 return name
1428 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1443 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1429
1444
1430 def mktempcopy(name, emptyok=False, createmode=None):
1445 def mktempcopy(name, emptyok=False, createmode=None):
1431 """Create a temporary file with the same contents from name
1446 """Create a temporary file with the same contents from name
1432
1447
1433 The permission bits are copied from the original file.
1448 The permission bits are copied from the original file.
1434
1449
1435 If the temporary file is going to be truncated immediately, you
1450 If the temporary file is going to be truncated immediately, you
1436 can use emptyok=True as an optimization.
1451 can use emptyok=True as an optimization.
1437
1452
1438 Returns the name of the temporary file.
1453 Returns the name of the temporary file.
1439 """
1454 """
1440 d, fn = os.path.split(name)
1455 d, fn = os.path.split(name)
1441 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1456 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1442 os.close(fd)
1457 os.close(fd)
1443 # Temporary files are created with mode 0600, which is usually not
1458 # Temporary files are created with mode 0600, which is usually not
1444 # what we want. If the original file already exists, just copy
1459 # what we want. If the original file already exists, just copy
1445 # its mode. Otherwise, manually obey umask.
1460 # its mode. Otherwise, manually obey umask.
1446 try:
1461 try:
1447 st_mode = os.lstat(name).st_mode & 0777
1462 st_mode = os.lstat(name).st_mode & 0777
1448 except OSError, inst:
1463 except OSError, inst:
1449 if inst.errno != errno.ENOENT:
1464 if inst.errno != errno.ENOENT:
1450 raise
1465 raise
1451 st_mode = createmode
1466 st_mode = createmode
1452 if st_mode is None:
1467 if st_mode is None:
1453 st_mode = ~_umask
1468 st_mode = ~_umask
1454 st_mode &= 0666
1469 st_mode &= 0666
1455 os.chmod(temp, st_mode)
1470 os.chmod(temp, st_mode)
1456 if emptyok:
1471 if emptyok:
1457 return temp
1472 return temp
1458 try:
1473 try:
1459 try:
1474 try:
1460 ifp = posixfile(name, "rb")
1475 ifp = posixfile(name, "rb")
1461 except IOError, inst:
1476 except IOError, inst:
1462 if inst.errno == errno.ENOENT:
1477 if inst.errno == errno.ENOENT:
1463 return temp
1478 return temp
1464 if not getattr(inst, 'filename', None):
1479 if not getattr(inst, 'filename', None):
1465 inst.filename = name
1480 inst.filename = name
1466 raise
1481 raise
1467 ofp = posixfile(temp, "wb")
1482 ofp = posixfile(temp, "wb")
1468 for chunk in filechunkiter(ifp):
1483 for chunk in filechunkiter(ifp):
1469 ofp.write(chunk)
1484 ofp.write(chunk)
1470 ifp.close()
1485 ifp.close()
1471 ofp.close()
1486 ofp.close()
1472 except:
1487 except:
1473 try: os.unlink(temp)
1488 try: os.unlink(temp)
1474 except: pass
1489 except: pass
1475 raise
1490 raise
1476 return temp
1491 return temp
1477
1492
1478 class atomictempfile(posixfile):
1493 class atomictempfile(posixfile):
1479 """file-like object that atomically updates a file
1494 """file-like object that atomically updates a file
1480
1495
1481 All writes will be redirected to a temporary copy of the original
1496 All writes will be redirected to a temporary copy of the original
1482 file. When rename is called, the copy is renamed to the original
1497 file. When rename is called, the copy is renamed to the original
1483 name, making the changes visible.
1498 name, making the changes visible.
1484 """
1499 """
1485 def __init__(self, name, mode, createmode):
1500 def __init__(self, name, mode, createmode):
1486 self.__name = name
1501 self.__name = name
1487 self.temp = mktempcopy(name, emptyok=('w' in mode),
1502 self.temp = mktempcopy(name, emptyok=('w' in mode),
1488 createmode=createmode)
1503 createmode=createmode)
1489 posixfile.__init__(self, self.temp, mode)
1504 posixfile.__init__(self, self.temp, mode)
1490
1505
1491 def rename(self):
1506 def rename(self):
1492 if not self.closed:
1507 if not self.closed:
1493 posixfile.close(self)
1508 posixfile.close(self)
1494 rename(self.temp, localpath(self.__name))
1509 rename(self.temp, localpath(self.__name))
1495
1510
1496 def __del__(self):
1511 def __del__(self):
1497 if not self.closed:
1512 if not self.closed:
1498 try:
1513 try:
1499 os.unlink(self.temp)
1514 os.unlink(self.temp)
1500 except: pass
1515 except: pass
1501 posixfile.close(self)
1516 posixfile.close(self)
1502
1517
1503 def makedirs(name, mode=None):
1518 def makedirs(name, mode=None):
1504 """recursive directory creation with parent mode inheritance"""
1519 """recursive directory creation with parent mode inheritance"""
1505 try:
1520 try:
1506 os.mkdir(name)
1521 os.mkdir(name)
1507 if mode is not None:
1522 if mode is not None:
1508 os.chmod(name, mode)
1523 os.chmod(name, mode)
1509 return
1524 return
1510 except OSError, err:
1525 except OSError, err:
1511 if err.errno == errno.EEXIST:
1526 if err.errno == errno.EEXIST:
1512 return
1527 return
1513 if err.errno != errno.ENOENT:
1528 if err.errno != errno.ENOENT:
1514 raise
1529 raise
1515 parent = os.path.abspath(os.path.dirname(name))
1530 parent = os.path.abspath(os.path.dirname(name))
1516 makedirs(parent, mode)
1531 makedirs(parent, mode)
1517 makedirs(name, mode)
1532 makedirs(name, mode)
1518
1533
1519 class opener(object):
1534 class opener(object):
1520 """Open files relative to a base directory
1535 """Open files relative to a base directory
1521
1536
1522 This class is used to hide the details of COW semantics and
1537 This class is used to hide the details of COW semantics and
1523 remote file access from higher level code.
1538 remote file access from higher level code.
1524 """
1539 """
1525 def __init__(self, base, audit=True):
1540 def __init__(self, base, audit=True):
1526 self.base = base
1541 self.base = base
1527 if audit:
1542 if audit:
1528 self.audit_path = path_auditor(base)
1543 self.audit_path = path_auditor(base)
1529 else:
1544 else:
1530 self.audit_path = always
1545 self.audit_path = always
1531 self.createmode = None
1546 self.createmode = None
1532
1547
1533 def __getattr__(self, name):
1548 def __getattr__(self, name):
1534 if name == '_can_symlink':
1549 if name == '_can_symlink':
1535 self._can_symlink = checklink(self.base)
1550 self._can_symlink = checklink(self.base)
1536 return self._can_symlink
1551 return self._can_symlink
1537 raise AttributeError(name)
1552 raise AttributeError(name)
1538
1553
1539 def _fixfilemode(self, name):
1554 def _fixfilemode(self, name):
1540 if self.createmode is None:
1555 if self.createmode is None:
1541 return
1556 return
1542 os.chmod(name, self.createmode & 0666)
1557 os.chmod(name, self.createmode & 0666)
1543
1558
1544 def __call__(self, path, mode="r", text=False, atomictemp=False):
1559 def __call__(self, path, mode="r", text=False, atomictemp=False):
1545 self.audit_path(path)
1560 self.audit_path(path)
1546 f = os.path.join(self.base, path)
1561 f = os.path.join(self.base, path)
1547
1562
1548 if not text and "b" not in mode:
1563 if not text and "b" not in mode:
1549 mode += "b" # for that other OS
1564 mode += "b" # for that other OS
1550
1565
1551 nlink = -1
1566 nlink = -1
1552 if mode not in ("r", "rb"):
1567 if mode not in ("r", "rb"):
1553 try:
1568 try:
1554 nlink = nlinks(f)
1569 nlink = nlinks(f)
1555 except OSError:
1570 except OSError:
1556 nlink = 0
1571 nlink = 0
1557 d = os.path.dirname(f)
1572 d = os.path.dirname(f)
1558 if not os.path.isdir(d):
1573 if not os.path.isdir(d):
1559 makedirs(d, self.createmode)
1574 makedirs(d, self.createmode)
1560 if atomictemp:
1575 if atomictemp:
1561 return atomictempfile(f, mode, self.createmode)
1576 return atomictempfile(f, mode, self.createmode)
1562 if nlink > 1:
1577 if nlink > 1:
1563 rename(mktempcopy(f), f)
1578 rename(mktempcopy(f), f)
1564 fp = posixfile(f, mode)
1579 fp = posixfile(f, mode)
1565 if nlink == 0:
1580 if nlink == 0:
1566 self._fixfilemode(f)
1581 self._fixfilemode(f)
1567 return fp
1582 return fp
1568
1583
1569 def symlink(self, src, dst):
1584 def symlink(self, src, dst):
1570 self.audit_path(dst)
1585 self.audit_path(dst)
1571 linkname = os.path.join(self.base, dst)
1586 linkname = os.path.join(self.base, dst)
1572 try:
1587 try:
1573 os.unlink(linkname)
1588 os.unlink(linkname)
1574 except OSError:
1589 except OSError:
1575 pass
1590 pass
1576
1591
1577 dirname = os.path.dirname(linkname)
1592 dirname = os.path.dirname(linkname)
1578 if not os.path.exists(dirname):
1593 if not os.path.exists(dirname):
1579 makedirs(dirname, self.createmode)
1594 makedirs(dirname, self.createmode)
1580
1595
1581 if self._can_symlink:
1596 if self._can_symlink:
1582 try:
1597 try:
1583 os.symlink(src, linkname)
1598 os.symlink(src, linkname)
1584 except OSError, err:
1599 except OSError, err:
1585 raise OSError(err.errno, _('could not symlink to %r: %s') %
1600 raise OSError(err.errno, _('could not symlink to %r: %s') %
1586 (src, err.strerror), linkname)
1601 (src, err.strerror), linkname)
1587 else:
1602 else:
1588 f = self(dst, "w")
1603 f = self(dst, "w")
1589 f.write(src)
1604 f.write(src)
1590 f.close()
1605 f.close()
1591 self._fixfilemode(dst)
1606 self._fixfilemode(dst)
1592
1607
1593 class chunkbuffer(object):
1608 class chunkbuffer(object):
1594 """Allow arbitrary sized chunks of data to be efficiently read from an
1609 """Allow arbitrary sized chunks of data to be efficiently read from an
1595 iterator over chunks of arbitrary size."""
1610 iterator over chunks of arbitrary size."""
1596
1611
1597 def __init__(self, in_iter):
1612 def __init__(self, in_iter):
1598 """in_iter is the iterator that's iterating over the input chunks.
1613 """in_iter is the iterator that's iterating over the input chunks.
1599 targetsize is how big a buffer to try to maintain."""
1614 targetsize is how big a buffer to try to maintain."""
1600 self.iter = iter(in_iter)
1615 self.iter = iter(in_iter)
1601 self.buf = ''
1616 self.buf = ''
1602 self.targetsize = 2**16
1617 self.targetsize = 2**16
1603
1618
1604 def read(self, l):
1619 def read(self, l):
1605 """Read L bytes of data from the iterator of chunks of data.
1620 """Read L bytes of data from the iterator of chunks of data.
1606 Returns less than L bytes if the iterator runs dry."""
1621 Returns less than L bytes if the iterator runs dry."""
1607 if l > len(self.buf) and self.iter:
1622 if l > len(self.buf) and self.iter:
1608 # Clamp to a multiple of self.targetsize
1623 # Clamp to a multiple of self.targetsize
1609 targetsize = max(l, self.targetsize)
1624 targetsize = max(l, self.targetsize)
1610 collector = cStringIO.StringIO()
1625 collector = cStringIO.StringIO()
1611 collector.write(self.buf)
1626 collector.write(self.buf)
1612 collected = len(self.buf)
1627 collected = len(self.buf)
1613 for chunk in self.iter:
1628 for chunk in self.iter:
1614 collector.write(chunk)
1629 collector.write(chunk)
1615 collected += len(chunk)
1630 collected += len(chunk)
1616 if collected >= targetsize:
1631 if collected >= targetsize:
1617 break
1632 break
1618 if collected < targetsize:
1633 if collected < targetsize:
1619 self.iter = False
1634 self.iter = False
1620 self.buf = collector.getvalue()
1635 self.buf = collector.getvalue()
1621 if len(self.buf) == l:
1636 if len(self.buf) == l:
1622 s, self.buf = str(self.buf), ''
1637 s, self.buf = str(self.buf), ''
1623 else:
1638 else:
1624 s, self.buf = self.buf[:l], buffer(self.buf, l)
1639 s, self.buf = self.buf[:l], buffer(self.buf, l)
1625 return s
1640 return s
1626
1641
1627 def filechunkiter(f, size=65536, limit=None):
1642 def filechunkiter(f, size=65536, limit=None):
1628 """Create a generator that produces the data in the file size
1643 """Create a generator that produces the data in the file size
1629 (default 65536) bytes at a time, up to optional limit (default is
1644 (default 65536) bytes at a time, up to optional limit (default is
1630 to read all data). Chunks may be less than size bytes if the
1645 to read all data). Chunks may be less than size bytes if the
1631 chunk is the last chunk in the file, or the file is a socket or
1646 chunk is the last chunk in the file, or the file is a socket or
1632 some other type of file that sometimes reads less data than is
1647 some other type of file that sometimes reads less data than is
1633 requested."""
1648 requested."""
1634 assert size >= 0
1649 assert size >= 0
1635 assert limit is None or limit >= 0
1650 assert limit is None or limit >= 0
1636 while True:
1651 while True:
1637 if limit is None: nbytes = size
1652 if limit is None: nbytes = size
1638 else: nbytes = min(limit, size)
1653 else: nbytes = min(limit, size)
1639 s = nbytes and f.read(nbytes)
1654 s = nbytes and f.read(nbytes)
1640 if not s: break
1655 if not s: break
1641 if limit: limit -= len(s)
1656 if limit: limit -= len(s)
1642 yield s
1657 yield s
1643
1658
1644 def makedate():
1659 def makedate():
1645 lt = time.localtime()
1660 lt = time.localtime()
1646 if lt[8] == 1 and time.daylight:
1661 if lt[8] == 1 and time.daylight:
1647 tz = time.altzone
1662 tz = time.altzone
1648 else:
1663 else:
1649 tz = time.timezone
1664 tz = time.timezone
1650 return time.mktime(lt), tz
1665 return time.mktime(lt), tz
1651
1666
1652 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1667 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1653 """represent a (unixtime, offset) tuple as a localized time.
1668 """represent a (unixtime, offset) tuple as a localized time.
1654 unixtime is seconds since the epoch, and offset is the time zone's
1669 unixtime is seconds since the epoch, and offset is the time zone's
1655 number of seconds away from UTC. if timezone is false, do not
1670 number of seconds away from UTC. if timezone is false, do not
1656 append time zone to string."""
1671 append time zone to string."""
1657 t, tz = date or makedate()
1672 t, tz = date or makedate()
1658 if "%1" in format or "%2" in format:
1673 if "%1" in format or "%2" in format:
1659 sign = (tz > 0) and "-" or "+"
1674 sign = (tz > 0) and "-" or "+"
1660 minutes = abs(tz) / 60
1675 minutes = abs(tz) / 60
1661 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1676 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1662 format = format.replace("%2", "%02d" % (minutes % 60))
1677 format = format.replace("%2", "%02d" % (minutes % 60))
1663 s = time.strftime(format, time.gmtime(float(t) - tz))
1678 s = time.strftime(format, time.gmtime(float(t) - tz))
1664 return s
1679 return s
1665
1680
1666 def shortdate(date=None):
1681 def shortdate(date=None):
1667 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1682 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1668 return datestr(date, format='%Y-%m-%d')
1683 return datestr(date, format='%Y-%m-%d')
1669
1684
1670 def strdate(string, format, defaults=[]):
1685 def strdate(string, format, defaults=[]):
1671 """parse a localized time string and return a (unixtime, offset) tuple.
1686 """parse a localized time string and return a (unixtime, offset) tuple.
1672 if the string cannot be parsed, ValueError is raised."""
1687 if the string cannot be parsed, ValueError is raised."""
1673 def timezone(string):
1688 def timezone(string):
1674 tz = string.split()[-1]
1689 tz = string.split()[-1]
1675 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1690 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1676 sign = (tz[0] == "+") and 1 or -1
1691 sign = (tz[0] == "+") and 1 or -1
1677 hours = int(tz[1:3])
1692 hours = int(tz[1:3])
1678 minutes = int(tz[3:5])
1693 minutes = int(tz[3:5])
1679 return -sign * (hours * 60 + minutes) * 60
1694 return -sign * (hours * 60 + minutes) * 60
1680 if tz == "GMT" or tz == "UTC":
1695 if tz == "GMT" or tz == "UTC":
1681 return 0
1696 return 0
1682 return None
1697 return None
1683
1698
1684 # NOTE: unixtime = localunixtime + offset
1699 # NOTE: unixtime = localunixtime + offset
1685 offset, date = timezone(string), string
1700 offset, date = timezone(string), string
1686 if offset != None:
1701 if offset != None:
1687 date = " ".join(string.split()[:-1])
1702 date = " ".join(string.split()[:-1])
1688
1703
1689 # add missing elements from defaults
1704 # add missing elements from defaults
1690 for part in defaults:
1705 for part in defaults:
1691 found = [True for p in part if ("%"+p) in format]
1706 found = [True for p in part if ("%"+p) in format]
1692 if not found:
1707 if not found:
1693 date += "@" + defaults[part]
1708 date += "@" + defaults[part]
1694 format += "@%" + part[0]
1709 format += "@%" + part[0]
1695
1710
1696 timetuple = time.strptime(date, format)
1711 timetuple = time.strptime(date, format)
1697 localunixtime = int(calendar.timegm(timetuple))
1712 localunixtime = int(calendar.timegm(timetuple))
1698 if offset is None:
1713 if offset is None:
1699 # local timezone
1714 # local timezone
1700 unixtime = int(time.mktime(timetuple))
1715 unixtime = int(time.mktime(timetuple))
1701 offset = unixtime - localunixtime
1716 offset = unixtime - localunixtime
1702 else:
1717 else:
1703 unixtime = localunixtime + offset
1718 unixtime = localunixtime + offset
1704 return unixtime, offset
1719 return unixtime, offset
1705
1720
1706 def parsedate(date, formats=None, defaults=None):
1721 def parsedate(date, formats=None, defaults=None):
1707 """parse a localized date/time string and return a (unixtime, offset) tuple.
1722 """parse a localized date/time string and return a (unixtime, offset) tuple.
1708
1723
1709 The date may be a "unixtime offset" string or in one of the specified
1724 The date may be a "unixtime offset" string or in one of the specified
1710 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1725 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1711 """
1726 """
1712 if not date:
1727 if not date:
1713 return 0, 0
1728 return 0, 0
1714 if isinstance(date, tuple) and len(date) == 2:
1729 if isinstance(date, tuple) and len(date) == 2:
1715 return date
1730 return date
1716 if not formats:
1731 if not formats:
1717 formats = defaultdateformats
1732 formats = defaultdateformats
1718 date = date.strip()
1733 date = date.strip()
1719 try:
1734 try:
1720 when, offset = map(int, date.split(' '))
1735 when, offset = map(int, date.split(' '))
1721 except ValueError:
1736 except ValueError:
1722 # fill out defaults
1737 # fill out defaults
1723 if not defaults:
1738 if not defaults:
1724 defaults = {}
1739 defaults = {}
1725 now = makedate()
1740 now = makedate()
1726 for part in "d mb yY HI M S".split():
1741 for part in "d mb yY HI M S".split():
1727 if part not in defaults:
1742 if part not in defaults:
1728 if part[0] in "HMS":
1743 if part[0] in "HMS":
1729 defaults[part] = "00"
1744 defaults[part] = "00"
1730 else:
1745 else:
1731 defaults[part] = datestr(now, "%" + part[0])
1746 defaults[part] = datestr(now, "%" + part[0])
1732
1747
1733 for format in formats:
1748 for format in formats:
1734 try:
1749 try:
1735 when, offset = strdate(date, format, defaults)
1750 when, offset = strdate(date, format, defaults)
1736 except (ValueError, OverflowError):
1751 except (ValueError, OverflowError):
1737 pass
1752 pass
1738 else:
1753 else:
1739 break
1754 break
1740 else:
1755 else:
1741 raise Abort(_('invalid date: %r ') % date)
1756 raise Abort(_('invalid date: %r ') % date)
1742 # validate explicit (probably user-specified) date and
1757 # validate explicit (probably user-specified) date and
1743 # time zone offset. values must fit in signed 32 bits for
1758 # time zone offset. values must fit in signed 32 bits for
1744 # current 32-bit linux runtimes. timezones go from UTC-12
1759 # current 32-bit linux runtimes. timezones go from UTC-12
1745 # to UTC+14
1760 # to UTC+14
1746 if abs(when) > 0x7fffffff:
1761 if abs(when) > 0x7fffffff:
1747 raise Abort(_('date exceeds 32 bits: %d') % when)
1762 raise Abort(_('date exceeds 32 bits: %d') % when)
1748 if offset < -50400 or offset > 43200:
1763 if offset < -50400 or offset > 43200:
1749 raise Abort(_('impossible time zone offset: %d') % offset)
1764 raise Abort(_('impossible time zone offset: %d') % offset)
1750 return when, offset
1765 return when, offset
1751
1766
1752 def matchdate(date):
1767 def matchdate(date):
1753 """Return a function that matches a given date match specifier
1768 """Return a function that matches a given date match specifier
1754
1769
1755 Formats include:
1770 Formats include:
1756
1771
1757 '{date}' match a given date to the accuracy provided
1772 '{date}' match a given date to the accuracy provided
1758
1773
1759 '<{date}' on or before a given date
1774 '<{date}' on or before a given date
1760
1775
1761 '>{date}' on or after a given date
1776 '>{date}' on or after a given date
1762
1777
1763 """
1778 """
1764
1779
1765 def lower(date):
1780 def lower(date):
1766 d = dict(mb="1", d="1")
1781 d = dict(mb="1", d="1")
1767 return parsedate(date, extendeddateformats, d)[0]
1782 return parsedate(date, extendeddateformats, d)[0]
1768
1783
1769 def upper(date):
1784 def upper(date):
1770 d = dict(mb="12", HI="23", M="59", S="59")
1785 d = dict(mb="12", HI="23", M="59", S="59")
1771 for days in "31 30 29".split():
1786 for days in "31 30 29".split():
1772 try:
1787 try:
1773 d["d"] = days
1788 d["d"] = days
1774 return parsedate(date, extendeddateformats, d)[0]
1789 return parsedate(date, extendeddateformats, d)[0]
1775 except:
1790 except:
1776 pass
1791 pass
1777 d["d"] = "28"
1792 d["d"] = "28"
1778 return parsedate(date, extendeddateformats, d)[0]
1793 return parsedate(date, extendeddateformats, d)[0]
1779
1794
1780 if date[0] == "<":
1795 if date[0] == "<":
1781 when = upper(date[1:])
1796 when = upper(date[1:])
1782 return lambda x: x <= when
1797 return lambda x: x <= when
1783 elif date[0] == ">":
1798 elif date[0] == ">":
1784 when = lower(date[1:])
1799 when = lower(date[1:])
1785 return lambda x: x >= when
1800 return lambda x: x >= when
1786 elif date[0] == "-":
1801 elif date[0] == "-":
1787 try:
1802 try:
1788 days = int(date[1:])
1803 days = int(date[1:])
1789 except ValueError:
1804 except ValueError:
1790 raise Abort(_("invalid day spec: %s") % date[1:])
1805 raise Abort(_("invalid day spec: %s") % date[1:])
1791 when = makedate()[0] - days * 3600 * 24
1806 when = makedate()[0] - days * 3600 * 24
1792 return lambda x: x >= when
1807 return lambda x: x >= when
1793 elif " to " in date:
1808 elif " to " in date:
1794 a, b = date.split(" to ")
1809 a, b = date.split(" to ")
1795 start, stop = lower(a), upper(b)
1810 start, stop = lower(a), upper(b)
1796 return lambda x: x >= start and x <= stop
1811 return lambda x: x >= start and x <= stop
1797 else:
1812 else:
1798 start, stop = lower(date), upper(date)
1813 start, stop = lower(date), upper(date)
1799 return lambda x: x >= start and x <= stop
1814 return lambda x: x >= start and x <= stop
1800
1815
1801 def shortuser(user):
1816 def shortuser(user):
1802 """Return a short representation of a user name or email address."""
1817 """Return a short representation of a user name or email address."""
1803 f = user.find('@')
1818 f = user.find('@')
1804 if f >= 0:
1819 if f >= 0:
1805 user = user[:f]
1820 user = user[:f]
1806 f = user.find('<')
1821 f = user.find('<')
1807 if f >= 0:
1822 if f >= 0:
1808 user = user[f+1:]
1823 user = user[f+1:]
1809 f = user.find(' ')
1824 f = user.find(' ')
1810 if f >= 0:
1825 if f >= 0:
1811 user = user[:f]
1826 user = user[:f]
1812 f = user.find('.')
1827 f = user.find('.')
1813 if f >= 0:
1828 if f >= 0:
1814 user = user[:f]
1829 user = user[:f]
1815 return user
1830 return user
1816
1831
1817 def email(author):
1832 def email(author):
1818 '''get email of author.'''
1833 '''get email of author.'''
1819 r = author.find('>')
1834 r = author.find('>')
1820 if r == -1: r = None
1835 if r == -1: r = None
1821 return author[author.find('<')+1:r]
1836 return author[author.find('<')+1:r]
1822
1837
1823 def ellipsis(text, maxlength=400):
1838 def ellipsis(text, maxlength=400):
1824 """Trim string to at most maxlength (default: 400) characters."""
1839 """Trim string to at most maxlength (default: 400) characters."""
1825 if len(text) <= maxlength:
1840 if len(text) <= maxlength:
1826 return text
1841 return text
1827 else:
1842 else:
1828 return "%s..." % (text[:maxlength-3])
1843 return "%s..." % (text[:maxlength-3])
1829
1844
1830 def walkrepos(path, followsym=False, seen_dirs=None):
1845 def walkrepos(path, followsym=False, seen_dirs=None):
1831 '''yield every hg repository under path, recursively.'''
1846 '''yield every hg repository under path, recursively.'''
1832 def errhandler(err):
1847 def errhandler(err):
1833 if err.filename == path:
1848 if err.filename == path:
1834 raise err
1849 raise err
1835 if followsym and hasattr(os.path, 'samestat'):
1850 if followsym and hasattr(os.path, 'samestat'):
1836 def _add_dir_if_not_there(dirlst, dirname):
1851 def _add_dir_if_not_there(dirlst, dirname):
1837 match = False
1852 match = False
1838 samestat = os.path.samestat
1853 samestat = os.path.samestat
1839 dirstat = os.stat(dirname)
1854 dirstat = os.stat(dirname)
1840 for lstdirstat in dirlst:
1855 for lstdirstat in dirlst:
1841 if samestat(dirstat, lstdirstat):
1856 if samestat(dirstat, lstdirstat):
1842 match = True
1857 match = True
1843 break
1858 break
1844 if not match:
1859 if not match:
1845 dirlst.append(dirstat)
1860 dirlst.append(dirstat)
1846 return not match
1861 return not match
1847 else:
1862 else:
1848 followsym = False
1863 followsym = False
1849
1864
1850 if (seen_dirs is None) and followsym:
1865 if (seen_dirs is None) and followsym:
1851 seen_dirs = []
1866 seen_dirs = []
1852 _add_dir_if_not_there(seen_dirs, path)
1867 _add_dir_if_not_there(seen_dirs, path)
1853 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1868 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1854 if '.hg' in dirs:
1869 if '.hg' in dirs:
1855 dirs.remove('.hg') # don't recurse inside the .hg directory
1870 dirs.remove('.hg') # don't recurse inside the .hg directory
1856 yield root # found a repository
1871 yield root # found a repository
1857 qroot = os.path.join(root, '.hg', 'patches')
1872 qroot = os.path.join(root, '.hg', 'patches')
1858 if os.path.isdir(os.path.join(qroot, '.hg')):
1873 if os.path.isdir(os.path.join(qroot, '.hg')):
1859 yield qroot # we have a patch queue repo here
1874 yield qroot # we have a patch queue repo here
1860 elif followsym:
1875 elif followsym:
1861 newdirs = []
1876 newdirs = []
1862 for d in dirs:
1877 for d in dirs:
1863 fname = os.path.join(root, d)
1878 fname = os.path.join(root, d)
1864 if _add_dir_if_not_there(seen_dirs, fname):
1879 if _add_dir_if_not_there(seen_dirs, fname):
1865 if os.path.islink(fname):
1880 if os.path.islink(fname):
1866 for hgname in walkrepos(fname, True, seen_dirs):
1881 for hgname in walkrepos(fname, True, seen_dirs):
1867 yield hgname
1882 yield hgname
1868 else:
1883 else:
1869 newdirs.append(d)
1884 newdirs.append(d)
1870 dirs[:] = newdirs
1885 dirs[:] = newdirs
1871
1886
1872 _rcpath = None
1887 _rcpath = None
1873
1888
1874 def os_rcpath():
1889 def os_rcpath():
1875 '''return default os-specific hgrc search path'''
1890 '''return default os-specific hgrc search path'''
1876 path = system_rcpath()
1891 path = system_rcpath()
1877 path.extend(user_rcpath())
1892 path.extend(user_rcpath())
1878 path = [os.path.normpath(f) for f in path]
1893 path = [os.path.normpath(f) for f in path]
1879 return path
1894 return path
1880
1895
1881 def rcpath():
1896 def rcpath():
1882 '''return hgrc search path. if env var HGRCPATH is set, use it.
1897 '''return hgrc search path. if env var HGRCPATH is set, use it.
1883 for each item in path, if directory, use files ending in .rc,
1898 for each item in path, if directory, use files ending in .rc,
1884 else use item.
1899 else use item.
1885 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1900 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1886 if no HGRCPATH, use default os-specific path.'''
1901 if no HGRCPATH, use default os-specific path.'''
1887 global _rcpath
1902 global _rcpath
1888 if _rcpath is None:
1903 if _rcpath is None:
1889 if 'HGRCPATH' in os.environ:
1904 if 'HGRCPATH' in os.environ:
1890 _rcpath = []
1905 _rcpath = []
1891 for p in os.environ['HGRCPATH'].split(os.pathsep):
1906 for p in os.environ['HGRCPATH'].split(os.pathsep):
1892 if not p: continue
1907 if not p: continue
1893 if os.path.isdir(p):
1908 if os.path.isdir(p):
1894 for f, kind in osutil.listdir(p):
1909 for f, kind in osutil.listdir(p):
1895 if f.endswith('.rc'):
1910 if f.endswith('.rc'):
1896 _rcpath.append(os.path.join(p, f))
1911 _rcpath.append(os.path.join(p, f))
1897 else:
1912 else:
1898 _rcpath.append(p)
1913 _rcpath.append(p)
1899 else:
1914 else:
1900 _rcpath = os_rcpath()
1915 _rcpath = os_rcpath()
1901 return _rcpath
1916 return _rcpath
1902
1917
1903 def bytecount(nbytes):
1918 def bytecount(nbytes):
1904 '''return byte count formatted as readable string, with units'''
1919 '''return byte count formatted as readable string, with units'''
1905
1920
1906 units = (
1921 units = (
1907 (100, 1<<30, _('%.0f GB')),
1922 (100, 1<<30, _('%.0f GB')),
1908 (10, 1<<30, _('%.1f GB')),
1923 (10, 1<<30, _('%.1f GB')),
1909 (1, 1<<30, _('%.2f GB')),
1924 (1, 1<<30, _('%.2f GB')),
1910 (100, 1<<20, _('%.0f MB')),
1925 (100, 1<<20, _('%.0f MB')),
1911 (10, 1<<20, _('%.1f MB')),
1926 (10, 1<<20, _('%.1f MB')),
1912 (1, 1<<20, _('%.2f MB')),
1927 (1, 1<<20, _('%.2f MB')),
1913 (100, 1<<10, _('%.0f KB')),
1928 (100, 1<<10, _('%.0f KB')),
1914 (10, 1<<10, _('%.1f KB')),
1929 (10, 1<<10, _('%.1f KB')),
1915 (1, 1<<10, _('%.2f KB')),
1930 (1, 1<<10, _('%.2f KB')),
1916 (1, 1, _('%.0f bytes')),
1931 (1, 1, _('%.0f bytes')),
1917 )
1932 )
1918
1933
1919 for multiplier, divisor, format in units:
1934 for multiplier, divisor, format in units:
1920 if nbytes >= divisor * multiplier:
1935 if nbytes >= divisor * multiplier:
1921 return format % (nbytes / float(divisor))
1936 return format % (nbytes / float(divisor))
1922 return units[-1][2] % nbytes
1937 return units[-1][2] % nbytes
1923
1938
1924 def drop_scheme(scheme, path):
1939 def drop_scheme(scheme, path):
1925 sc = scheme + ':'
1940 sc = scheme + ':'
1926 if path.startswith(sc):
1941 if path.startswith(sc):
1927 path = path[len(sc):]
1942 path = path[len(sc):]
1928 if path.startswith('//'):
1943 if path.startswith('//'):
1929 path = path[2:]
1944 path = path[2:]
1930 return path
1945 return path
1931
1946
1932 def uirepr(s):
1947 def uirepr(s):
1933 # Avoid double backslash in Windows path repr()
1948 # Avoid double backslash in Windows path repr()
1934 return repr(s).replace('\\\\', '\\')
1949 return repr(s).replace('\\\\', '\\')
General Comments 0
You need to be logged in to leave comments. Login now