##// END OF EJS Templates
dispatch: assign I/O descriptors from the request to the ui
Idan Kamara -
r14615:9fba795d default
parent child Browse files
Show More
@@ -1,693 +1,709
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
16 self.args = args
16 self.args = args
17 self.ui = ui
17 self.ui = ui
18 self.repo = repo
18 self.repo = repo
19
19
20 # input/output/error streams
20 # input/output/error streams
21 self.fin = fin
21 self.fin = fin
22 self.fout = fout
22 self.fout = fout
23 self.ferr = ferr
23 self.ferr = ferr
24
24
25 def run():
25 def run():
26 "run the command in sys.argv"
26 "run the command in sys.argv"
27 sys.exit(dispatch(request(sys.argv[1:])))
27 sys.exit(dispatch(request(sys.argv[1:])))
28
28
29 def dispatch(req):
29 def dispatch(req):
30 "run the command specified in req.args"
30 "run the command specified in req.args"
31 if req.ferr:
32 ferr = req.ferr
33 elif req.ui:
34 ferr = req.ui.ferr
35 else:
36 ferr = sys.stderr
37
31 try:
38 try:
32 if not req.ui:
39 if not req.ui:
33 req.ui = uimod.ui()
40 req.ui = uimod.ui()
34 if '--traceback' in req.args:
41 if '--traceback' in req.args:
35 req.ui.setconfig('ui', 'traceback', 'on')
42 req.ui.setconfig('ui', 'traceback', 'on')
43
44 # set ui streams from the request
45 if req.fin:
46 req.ui.fin = req.fin
47 if req.fout:
48 req.ui.fout = req.fout
49 if req.ferr:
50 req.ui.ferr = req.ferr
36 except util.Abort, inst:
51 except util.Abort, inst:
37 sys.stderr.write(_("abort: %s\n") % inst)
52 ferr.write(_("abort: %s\n") % inst)
38 if inst.hint:
53 if inst.hint:
39 sys.stderr.write(_("(%s)\n") % inst.hint)
54 ferr.write(_("(%s)\n") % inst.hint)
40 return -1
55 return -1
41 except error.ParseError, inst:
56 except error.ParseError, inst:
42 if len(inst.args) > 1:
57 if len(inst.args) > 1:
43 sys.stderr.write(_("hg: parse error at %s: %s\n") %
58 ferr.write(_("hg: parse error at %s: %s\n") %
44 (inst.args[1], inst.args[0]))
59 (inst.args[1], inst.args[0]))
45 else:
60 else:
46 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
47 return -1
62 return -1
63
48 return _runcatch(req)
64 return _runcatch(req)
49
65
50 def _runcatch(req):
66 def _runcatch(req):
51 def catchterm(*args):
67 def catchterm(*args):
52 raise error.SignalInterrupt
68 raise error.SignalInterrupt
53
69
54 ui = req.ui
70 ui = req.ui
55 try:
71 try:
56 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
57 num = getattr(signal, name, None)
73 num = getattr(signal, name, None)
58 if num:
74 if num:
59 signal.signal(num, catchterm)
75 signal.signal(num, catchterm)
60 except ValueError:
76 except ValueError:
61 pass # happens if called in a thread
77 pass # happens if called in a thread
62
78
63 try:
79 try:
64 try:
80 try:
65 # enter the debugger before command execution
81 # enter the debugger before command execution
66 if '--debugger' in req.args:
82 if '--debugger' in req.args:
67 ui.warn(_("entering debugger - "
83 ui.warn(_("entering debugger - "
68 "type c to continue starting hg or h for help\n"))
84 "type c to continue starting hg or h for help\n"))
69 pdb.set_trace()
85 pdb.set_trace()
70 try:
86 try:
71 return _dispatch(req)
87 return _dispatch(req)
72 finally:
88 finally:
73 ui.flush()
89 ui.flush()
74 except:
90 except:
75 # enter the debugger when we hit an exception
91 # enter the debugger when we hit an exception
76 if '--debugger' in req.args:
92 if '--debugger' in req.args:
77 traceback.print_exc()
93 traceback.print_exc()
78 pdb.post_mortem(sys.exc_info()[2])
94 pdb.post_mortem(sys.exc_info()[2])
79 ui.traceback()
95 ui.traceback()
80 raise
96 raise
81
97
82 # Global exception handling, alphabetically
98 # Global exception handling, alphabetically
83 # Mercurial-specific first, followed by built-in and library exceptions
99 # Mercurial-specific first, followed by built-in and library exceptions
84 except error.AmbiguousCommand, inst:
100 except error.AmbiguousCommand, inst:
85 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
86 (inst.args[0], " ".join(inst.args[1])))
102 (inst.args[0], " ".join(inst.args[1])))
87 except error.ParseError, inst:
103 except error.ParseError, inst:
88 if len(inst.args) > 1:
104 if len(inst.args) > 1:
89 ui.warn(_("hg: parse error at %s: %s\n") %
105 ui.warn(_("hg: parse error at %s: %s\n") %
90 (inst.args[1], inst.args[0]))
106 (inst.args[1], inst.args[0]))
91 else:
107 else:
92 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
93 return -1
109 return -1
94 except error.LockHeld, inst:
110 except error.LockHeld, inst:
95 if inst.errno == errno.ETIMEDOUT:
111 if inst.errno == errno.ETIMEDOUT:
96 reason = _('timed out waiting for lock held by %s') % inst.locker
112 reason = _('timed out waiting for lock held by %s') % inst.locker
97 else:
113 else:
98 reason = _('lock held by %s') % inst.locker
114 reason = _('lock held by %s') % inst.locker
99 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
100 except error.LockUnavailable, inst:
116 except error.LockUnavailable, inst:
101 ui.warn(_("abort: could not lock %s: %s\n") %
117 ui.warn(_("abort: could not lock %s: %s\n") %
102 (inst.desc or inst.filename, inst.strerror))
118 (inst.desc or inst.filename, inst.strerror))
103 except error.CommandError, inst:
119 except error.CommandError, inst:
104 if inst.args[0]:
120 if inst.args[0]:
105 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
106 commands.help_(ui, inst.args[0], full=False, command=True)
122 commands.help_(ui, inst.args[0], full=False, command=True)
107 else:
123 else:
108 ui.warn(_("hg: %s\n") % inst.args[1])
124 ui.warn(_("hg: %s\n") % inst.args[1])
109 commands.help_(ui, 'shortlist')
125 commands.help_(ui, 'shortlist')
110 except error.RepoError, inst:
126 except error.RepoError, inst:
111 ui.warn(_("abort: %s!\n") % inst)
127 ui.warn(_("abort: %s!\n") % inst)
112 except error.ResponseError, inst:
128 except error.ResponseError, inst:
113 ui.warn(_("abort: %s") % inst.args[0])
129 ui.warn(_("abort: %s") % inst.args[0])
114 if not isinstance(inst.args[1], basestring):
130 if not isinstance(inst.args[1], basestring):
115 ui.warn(" %r\n" % (inst.args[1],))
131 ui.warn(" %r\n" % (inst.args[1],))
116 elif not inst.args[1]:
132 elif not inst.args[1]:
117 ui.warn(_(" empty string\n"))
133 ui.warn(_(" empty string\n"))
118 else:
134 else:
119 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
135 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
120 except error.RevlogError, inst:
136 except error.RevlogError, inst:
121 ui.warn(_("abort: %s!\n") % inst)
137 ui.warn(_("abort: %s!\n") % inst)
122 except error.SignalInterrupt:
138 except error.SignalInterrupt:
123 ui.warn(_("killed!\n"))
139 ui.warn(_("killed!\n"))
124 except error.UnknownCommand, inst:
140 except error.UnknownCommand, inst:
125 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
141 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
126 try:
142 try:
127 # check if the command is in a disabled extension
143 # check if the command is in a disabled extension
128 # (but don't check for extensions themselves)
144 # (but don't check for extensions themselves)
129 commands.help_(ui, inst.args[0], unknowncmd=True)
145 commands.help_(ui, inst.args[0], unknowncmd=True)
130 except error.UnknownCommand:
146 except error.UnknownCommand:
131 commands.help_(ui, 'shortlist')
147 commands.help_(ui, 'shortlist')
132 except util.Abort, inst:
148 except util.Abort, inst:
133 ui.warn(_("abort: %s\n") % inst)
149 ui.warn(_("abort: %s\n") % inst)
134 if inst.hint:
150 if inst.hint:
135 ui.warn(_("(%s)\n") % inst.hint)
151 ui.warn(_("(%s)\n") % inst.hint)
136 except ImportError, inst:
152 except ImportError, inst:
137 ui.warn(_("abort: %s!\n") % inst)
153 ui.warn(_("abort: %s!\n") % inst)
138 m = str(inst).split()[-1]
154 m = str(inst).split()[-1]
139 if m in "mpatch bdiff".split():
155 if m in "mpatch bdiff".split():
140 ui.warn(_("(did you forget to compile extensions?)\n"))
156 ui.warn(_("(did you forget to compile extensions?)\n"))
141 elif m in "zlib".split():
157 elif m in "zlib".split():
142 ui.warn(_("(is your Python install correct?)\n"))
158 ui.warn(_("(is your Python install correct?)\n"))
143 except IOError, inst:
159 except IOError, inst:
144 if hasattr(inst, "code"):
160 if hasattr(inst, "code"):
145 ui.warn(_("abort: %s\n") % inst)
161 ui.warn(_("abort: %s\n") % inst)
146 elif hasattr(inst, "reason"):
162 elif hasattr(inst, "reason"):
147 try: # usually it is in the form (errno, strerror)
163 try: # usually it is in the form (errno, strerror)
148 reason = inst.reason.args[1]
164 reason = inst.reason.args[1]
149 except (AttributeError, IndexError):
165 except (AttributeError, IndexError):
150 # it might be anything, for example a string
166 # it might be anything, for example a string
151 reason = inst.reason
167 reason = inst.reason
152 ui.warn(_("abort: error: %s\n") % reason)
168 ui.warn(_("abort: error: %s\n") % reason)
153 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
169 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
154 if ui.debugflag:
170 if ui.debugflag:
155 ui.warn(_("broken pipe\n"))
171 ui.warn(_("broken pipe\n"))
156 elif getattr(inst, "strerror", None):
172 elif getattr(inst, "strerror", None):
157 if getattr(inst, "filename", None):
173 if getattr(inst, "filename", None):
158 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
174 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
159 else:
175 else:
160 ui.warn(_("abort: %s\n") % inst.strerror)
176 ui.warn(_("abort: %s\n") % inst.strerror)
161 else:
177 else:
162 raise
178 raise
163 except OSError, inst:
179 except OSError, inst:
164 if getattr(inst, "filename", None):
180 if getattr(inst, "filename", None):
165 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
181 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
166 else:
182 else:
167 ui.warn(_("abort: %s\n") % inst.strerror)
183 ui.warn(_("abort: %s\n") % inst.strerror)
168 except KeyboardInterrupt:
184 except KeyboardInterrupt:
169 try:
185 try:
170 ui.warn(_("interrupted!\n"))
186 ui.warn(_("interrupted!\n"))
171 except IOError, inst:
187 except IOError, inst:
172 if inst.errno == errno.EPIPE:
188 if inst.errno == errno.EPIPE:
173 if ui.debugflag:
189 if ui.debugflag:
174 ui.warn(_("\nbroken pipe\n"))
190 ui.warn(_("\nbroken pipe\n"))
175 else:
191 else:
176 raise
192 raise
177 except MemoryError:
193 except MemoryError:
178 ui.warn(_("abort: out of memory\n"))
194 ui.warn(_("abort: out of memory\n"))
179 except SystemExit, inst:
195 except SystemExit, inst:
180 # Commands shouldn't sys.exit directly, but give a return code.
196 # Commands shouldn't sys.exit directly, but give a return code.
181 # Just in case catch this and and pass exit code to caller.
197 # Just in case catch this and and pass exit code to caller.
182 return inst.code
198 return inst.code
183 except socket.error, inst:
199 except socket.error, inst:
184 ui.warn(_("abort: %s\n") % inst.args[-1])
200 ui.warn(_("abort: %s\n") % inst.args[-1])
185 except:
201 except:
186 ui.warn(_("** unknown exception encountered,"
202 ui.warn(_("** unknown exception encountered,"
187 " please report by visiting\n"))
203 " please report by visiting\n"))
188 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
204 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
189 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
205 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
190 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
206 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
191 % util.version())
207 % util.version())
192 ui.warn(_("** Extensions loaded: %s\n")
208 ui.warn(_("** Extensions loaded: %s\n")
193 % ", ".join([x[0] for x in extensions.extensions()]))
209 % ", ".join([x[0] for x in extensions.extensions()]))
194 raise
210 raise
195
211
196 return -1
212 return -1
197
213
198 def aliasargs(fn, givenargs):
214 def aliasargs(fn, givenargs):
199 args = getattr(fn, 'args', [])
215 args = getattr(fn, 'args', [])
200 if args and givenargs:
216 if args and givenargs:
201 cmd = ' '.join(map(util.shellquote, args))
217 cmd = ' '.join(map(util.shellquote, args))
202
218
203 nums = []
219 nums = []
204 def replacer(m):
220 def replacer(m):
205 num = int(m.group(1)) - 1
221 num = int(m.group(1)) - 1
206 nums.append(num)
222 nums.append(num)
207 return givenargs[num]
223 return givenargs[num]
208 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
224 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
209 givenargs = [x for i, x in enumerate(givenargs)
225 givenargs = [x for i, x in enumerate(givenargs)
210 if i not in nums]
226 if i not in nums]
211 args = shlex.split(cmd)
227 args = shlex.split(cmd)
212 return args + givenargs
228 return args + givenargs
213
229
214 class cmdalias(object):
230 class cmdalias(object):
215 def __init__(self, name, definition, cmdtable):
231 def __init__(self, name, definition, cmdtable):
216 self.name = self.cmd = name
232 self.name = self.cmd = name
217 self.cmdname = ''
233 self.cmdname = ''
218 self.definition = definition
234 self.definition = definition
219 self.args = []
235 self.args = []
220 self.opts = []
236 self.opts = []
221 self.help = ''
237 self.help = ''
222 self.norepo = True
238 self.norepo = True
223 self.badalias = False
239 self.badalias = False
224
240
225 try:
241 try:
226 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
242 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
227 for alias, e in cmdtable.iteritems():
243 for alias, e in cmdtable.iteritems():
228 if e is entry:
244 if e is entry:
229 self.cmd = alias
245 self.cmd = alias
230 break
246 break
231 self.shadows = True
247 self.shadows = True
232 except error.UnknownCommand:
248 except error.UnknownCommand:
233 self.shadows = False
249 self.shadows = False
234
250
235 if not self.definition:
251 if not self.definition:
236 def fn(ui, *args):
252 def fn(ui, *args):
237 ui.warn(_("no definition for alias '%s'\n") % self.name)
253 ui.warn(_("no definition for alias '%s'\n") % self.name)
238 return 1
254 return 1
239 self.fn = fn
255 self.fn = fn
240 self.badalias = True
256 self.badalias = True
241
257
242 return
258 return
243
259
244 if self.definition.startswith('!'):
260 if self.definition.startswith('!'):
245 self.shell = True
261 self.shell = True
246 def fn(ui, *args):
262 def fn(ui, *args):
247 env = {'HG_ARGS': ' '.join((self.name,) + args)}
263 env = {'HG_ARGS': ' '.join((self.name,) + args)}
248 def _checkvar(m):
264 def _checkvar(m):
249 if m.groups()[0] == '$':
265 if m.groups()[0] == '$':
250 return m.group()
266 return m.group()
251 elif int(m.groups()[0]) <= len(args):
267 elif int(m.groups()[0]) <= len(args):
252 return m.group()
268 return m.group()
253 else:
269 else:
254 ui.debug(_("No argument found for substitution "
270 ui.debug(_("No argument found for substitution "
255 "of %i variable in alias '%s' definition.")
271 "of %i variable in alias '%s' definition.")
256 % (int(m.groups()[0]), self.name))
272 % (int(m.groups()[0]), self.name))
257 return ''
273 return ''
258 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
274 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
259 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
275 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
260 replace['0'] = self.name
276 replace['0'] = self.name
261 replace['@'] = ' '.join(args)
277 replace['@'] = ' '.join(args)
262 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
278 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
263 return util.system(cmd, environ=env)
279 return util.system(cmd, environ=env)
264 self.fn = fn
280 self.fn = fn
265 return
281 return
266
282
267 args = shlex.split(self.definition)
283 args = shlex.split(self.definition)
268 self.cmdname = cmd = args.pop(0)
284 self.cmdname = cmd = args.pop(0)
269 args = map(util.expandpath, args)
285 args = map(util.expandpath, args)
270
286
271 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
287 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
272 if _earlygetopt([invalidarg], args):
288 if _earlygetopt([invalidarg], args):
273 def fn(ui, *args):
289 def fn(ui, *args):
274 ui.warn(_("error in definition for alias '%s': %s may only "
290 ui.warn(_("error in definition for alias '%s': %s may only "
275 "be given on the command line\n")
291 "be given on the command line\n")
276 % (self.name, invalidarg))
292 % (self.name, invalidarg))
277 return 1
293 return 1
278
294
279 self.fn = fn
295 self.fn = fn
280 self.badalias = True
296 self.badalias = True
281 return
297 return
282
298
283 try:
299 try:
284 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
300 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
285 if len(tableentry) > 2:
301 if len(tableentry) > 2:
286 self.fn, self.opts, self.help = tableentry
302 self.fn, self.opts, self.help = tableentry
287 else:
303 else:
288 self.fn, self.opts = tableentry
304 self.fn, self.opts = tableentry
289
305
290 self.args = aliasargs(self.fn, args)
306 self.args = aliasargs(self.fn, args)
291 if cmd not in commands.norepo.split(' '):
307 if cmd not in commands.norepo.split(' '):
292 self.norepo = False
308 self.norepo = False
293 if self.help.startswith("hg " + cmd):
309 if self.help.startswith("hg " + cmd):
294 # drop prefix in old-style help lines so hg shows the alias
310 # drop prefix in old-style help lines so hg shows the alias
295 self.help = self.help[4 + len(cmd):]
311 self.help = self.help[4 + len(cmd):]
296 self.__doc__ = self.fn.__doc__
312 self.__doc__ = self.fn.__doc__
297
313
298 except error.UnknownCommand:
314 except error.UnknownCommand:
299 def fn(ui, *args):
315 def fn(ui, *args):
300 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
316 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
301 % (self.name, cmd))
317 % (self.name, cmd))
302 try:
318 try:
303 # check if the command is in a disabled extension
319 # check if the command is in a disabled extension
304 commands.help_(ui, cmd, unknowncmd=True)
320 commands.help_(ui, cmd, unknowncmd=True)
305 except error.UnknownCommand:
321 except error.UnknownCommand:
306 pass
322 pass
307 return 1
323 return 1
308 self.fn = fn
324 self.fn = fn
309 self.badalias = True
325 self.badalias = True
310 except error.AmbiguousCommand:
326 except error.AmbiguousCommand:
311 def fn(ui, *args):
327 def fn(ui, *args):
312 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
328 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
313 % (self.name, cmd))
329 % (self.name, cmd))
314 return 1
330 return 1
315 self.fn = fn
331 self.fn = fn
316 self.badalias = True
332 self.badalias = True
317
333
318 def __call__(self, ui, *args, **opts):
334 def __call__(self, ui, *args, **opts):
319 if self.shadows:
335 if self.shadows:
320 ui.debug("alias '%s' shadows command '%s'\n" %
336 ui.debug("alias '%s' shadows command '%s'\n" %
321 (self.name, self.cmdname))
337 (self.name, self.cmdname))
322
338
323 if hasattr(self, 'shell'):
339 if hasattr(self, 'shell'):
324 return self.fn(ui, *args, **opts)
340 return self.fn(ui, *args, **opts)
325 else:
341 else:
326 try:
342 try:
327 util.checksignature(self.fn)(ui, *args, **opts)
343 util.checksignature(self.fn)(ui, *args, **opts)
328 except error.SignatureError:
344 except error.SignatureError:
329 args = ' '.join([self.cmdname] + self.args)
345 args = ' '.join([self.cmdname] + self.args)
330 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
346 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
331 raise
347 raise
332
348
333 def addaliases(ui, cmdtable):
349 def addaliases(ui, cmdtable):
334 # aliases are processed after extensions have been loaded, so they
350 # aliases are processed after extensions have been loaded, so they
335 # may use extension commands. Aliases can also use other alias definitions,
351 # may use extension commands. Aliases can also use other alias definitions,
336 # but only if they have been defined prior to the current definition.
352 # but only if they have been defined prior to the current definition.
337 for alias, definition in ui.configitems('alias'):
353 for alias, definition in ui.configitems('alias'):
338 aliasdef = cmdalias(alias, definition, cmdtable)
354 aliasdef = cmdalias(alias, definition, cmdtable)
339 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
355 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
340 if aliasdef.norepo:
356 if aliasdef.norepo:
341 commands.norepo += ' %s' % alias
357 commands.norepo += ' %s' % alias
342
358
343 def _parse(ui, args):
359 def _parse(ui, args):
344 options = {}
360 options = {}
345 cmdoptions = {}
361 cmdoptions = {}
346
362
347 try:
363 try:
348 args = fancyopts.fancyopts(args, commands.globalopts, options)
364 args = fancyopts.fancyopts(args, commands.globalopts, options)
349 except fancyopts.getopt.GetoptError, inst:
365 except fancyopts.getopt.GetoptError, inst:
350 raise error.CommandError(None, inst)
366 raise error.CommandError(None, inst)
351
367
352 if args:
368 if args:
353 cmd, args = args[0], args[1:]
369 cmd, args = args[0], args[1:]
354 aliases, entry = cmdutil.findcmd(cmd, commands.table,
370 aliases, entry = cmdutil.findcmd(cmd, commands.table,
355 ui.config("ui", "strict"))
371 ui.config("ui", "strict"))
356 cmd = aliases[0]
372 cmd = aliases[0]
357 args = aliasargs(entry[0], args)
373 args = aliasargs(entry[0], args)
358 defaults = ui.config("defaults", cmd)
374 defaults = ui.config("defaults", cmd)
359 if defaults:
375 if defaults:
360 args = map(util.expandpath, shlex.split(defaults)) + args
376 args = map(util.expandpath, shlex.split(defaults)) + args
361 c = list(entry[1])
377 c = list(entry[1])
362 else:
378 else:
363 cmd = None
379 cmd = None
364 c = []
380 c = []
365
381
366 # combine global options into local
382 # combine global options into local
367 for o in commands.globalopts:
383 for o in commands.globalopts:
368 c.append((o[0], o[1], options[o[1]], o[3]))
384 c.append((o[0], o[1], options[o[1]], o[3]))
369
385
370 try:
386 try:
371 args = fancyopts.fancyopts(args, c, cmdoptions, True)
387 args = fancyopts.fancyopts(args, c, cmdoptions, True)
372 except fancyopts.getopt.GetoptError, inst:
388 except fancyopts.getopt.GetoptError, inst:
373 raise error.CommandError(cmd, inst)
389 raise error.CommandError(cmd, inst)
374
390
375 # separate global options back out
391 # separate global options back out
376 for o in commands.globalopts:
392 for o in commands.globalopts:
377 n = o[1]
393 n = o[1]
378 options[n] = cmdoptions[n]
394 options[n] = cmdoptions[n]
379 del cmdoptions[n]
395 del cmdoptions[n]
380
396
381 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
397 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
382
398
383 def _parseconfig(ui, config):
399 def _parseconfig(ui, config):
384 """parse the --config options from the command line"""
400 """parse the --config options from the command line"""
385 for cfg in config:
401 for cfg in config:
386 try:
402 try:
387 name, value = cfg.split('=', 1)
403 name, value = cfg.split('=', 1)
388 section, name = name.split('.', 1)
404 section, name = name.split('.', 1)
389 if not section or not name:
405 if not section or not name:
390 raise IndexError
406 raise IndexError
391 ui.setconfig(section, name, value)
407 ui.setconfig(section, name, value)
392 except (IndexError, ValueError):
408 except (IndexError, ValueError):
393 raise util.Abort(_('malformed --config option: %r '
409 raise util.Abort(_('malformed --config option: %r '
394 '(use --config section.name=value)') % cfg)
410 '(use --config section.name=value)') % cfg)
395
411
396 def _earlygetopt(aliases, args):
412 def _earlygetopt(aliases, args):
397 """Return list of values for an option (or aliases).
413 """Return list of values for an option (or aliases).
398
414
399 The values are listed in the order they appear in args.
415 The values are listed in the order they appear in args.
400 The options and values are removed from args.
416 The options and values are removed from args.
401 """
417 """
402 try:
418 try:
403 argcount = args.index("--")
419 argcount = args.index("--")
404 except ValueError:
420 except ValueError:
405 argcount = len(args)
421 argcount = len(args)
406 shortopts = [opt for opt in aliases if len(opt) == 2]
422 shortopts = [opt for opt in aliases if len(opt) == 2]
407 values = []
423 values = []
408 pos = 0
424 pos = 0
409 while pos < argcount:
425 while pos < argcount:
410 if args[pos] in aliases:
426 if args[pos] in aliases:
411 if pos + 1 >= argcount:
427 if pos + 1 >= argcount:
412 # ignore and let getopt report an error if there is no value
428 # ignore and let getopt report an error if there is no value
413 break
429 break
414 del args[pos]
430 del args[pos]
415 values.append(args.pop(pos))
431 values.append(args.pop(pos))
416 argcount -= 2
432 argcount -= 2
417 elif args[pos][:2] in shortopts:
433 elif args[pos][:2] in shortopts:
418 # short option can have no following space, e.g. hg log -Rfoo
434 # short option can have no following space, e.g. hg log -Rfoo
419 values.append(args.pop(pos)[2:])
435 values.append(args.pop(pos)[2:])
420 argcount -= 1
436 argcount -= 1
421 else:
437 else:
422 pos += 1
438 pos += 1
423 return values
439 return values
424
440
425 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
441 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
426 # run pre-hook, and abort if it fails
442 # run pre-hook, and abort if it fails
427 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
443 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
428 pats=cmdpats, opts=cmdoptions)
444 pats=cmdpats, opts=cmdoptions)
429 if ret:
445 if ret:
430 return ret
446 return ret
431 ret = _runcommand(ui, options, cmd, d)
447 ret = _runcommand(ui, options, cmd, d)
432 # run post-hook, passing command result
448 # run post-hook, passing command result
433 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
449 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
434 result=ret, pats=cmdpats, opts=cmdoptions)
450 result=ret, pats=cmdpats, opts=cmdoptions)
435 return ret
451 return ret
436
452
437 def _getlocal(ui, rpath):
453 def _getlocal(ui, rpath):
438 """Return (path, local ui object) for the given target path.
454 """Return (path, local ui object) for the given target path.
439
455
440 Takes paths in [cwd]/.hg/hgrc into account."
456 Takes paths in [cwd]/.hg/hgrc into account."
441 """
457 """
442 try:
458 try:
443 wd = os.getcwd()
459 wd = os.getcwd()
444 except OSError, e:
460 except OSError, e:
445 raise util.Abort(_("error getting current working directory: %s") %
461 raise util.Abort(_("error getting current working directory: %s") %
446 e.strerror)
462 e.strerror)
447 path = cmdutil.findrepo(wd) or ""
463 path = cmdutil.findrepo(wd) or ""
448 if not path:
464 if not path:
449 lui = ui
465 lui = ui
450 else:
466 else:
451 lui = ui.copy()
467 lui = ui.copy()
452 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
468 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
453
469
454 if rpath:
470 if rpath:
455 path = lui.expandpath(rpath[-1])
471 path = lui.expandpath(rpath[-1])
456 lui = ui.copy()
472 lui = ui.copy()
457 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
473 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
458
474
459 return path, lui
475 return path, lui
460
476
461 def _checkshellalias(ui, args):
477 def _checkshellalias(ui, args):
462 cwd = os.getcwd()
478 cwd = os.getcwd()
463 norepo = commands.norepo
479 norepo = commands.norepo
464 options = {}
480 options = {}
465
481
466 try:
482 try:
467 args = fancyopts.fancyopts(args, commands.globalopts, options)
483 args = fancyopts.fancyopts(args, commands.globalopts, options)
468 except fancyopts.getopt.GetoptError:
484 except fancyopts.getopt.GetoptError:
469 return
485 return
470
486
471 if not args:
487 if not args:
472 return
488 return
473
489
474 _parseconfig(ui, options['config'])
490 _parseconfig(ui, options['config'])
475 if options['cwd']:
491 if options['cwd']:
476 os.chdir(options['cwd'])
492 os.chdir(options['cwd'])
477
493
478 path, lui = _getlocal(ui, [options['repository']])
494 path, lui = _getlocal(ui, [options['repository']])
479
495
480 cmdtable = commands.table.copy()
496 cmdtable = commands.table.copy()
481 addaliases(lui, cmdtable)
497 addaliases(lui, cmdtable)
482
498
483 cmd = args[0]
499 cmd = args[0]
484 try:
500 try:
485 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
501 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
486 except (error.AmbiguousCommand, error.UnknownCommand):
502 except (error.AmbiguousCommand, error.UnknownCommand):
487 commands.norepo = norepo
503 commands.norepo = norepo
488 os.chdir(cwd)
504 os.chdir(cwd)
489 return
505 return
490
506
491 cmd = aliases[0]
507 cmd = aliases[0]
492 fn = entry[0]
508 fn = entry[0]
493
509
494 if cmd and hasattr(fn, 'shell'):
510 if cmd and hasattr(fn, 'shell'):
495 d = lambda: fn(ui, *args[1:])
511 d = lambda: fn(ui, *args[1:])
496 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
512 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
497
513
498 commands.norepo = norepo
514 commands.norepo = norepo
499 os.chdir(cwd)
515 os.chdir(cwd)
500
516
501 _loaded = set()
517 _loaded = set()
502 def _dispatch(req):
518 def _dispatch(req):
503 args = req.args
519 args = req.args
504 ui = req.ui
520 ui = req.ui
505
521
506 shellaliasfn = _checkshellalias(ui, args)
522 shellaliasfn = _checkshellalias(ui, args)
507 if shellaliasfn:
523 if shellaliasfn:
508 return shellaliasfn()
524 return shellaliasfn()
509
525
510 # read --config before doing anything else
526 # read --config before doing anything else
511 # (e.g. to change trust settings for reading .hg/hgrc)
527 # (e.g. to change trust settings for reading .hg/hgrc)
512 _parseconfig(ui, _earlygetopt(['--config'], args))
528 _parseconfig(ui, _earlygetopt(['--config'], args))
513
529
514 # check for cwd
530 # check for cwd
515 cwd = _earlygetopt(['--cwd'], args)
531 cwd = _earlygetopt(['--cwd'], args)
516 if cwd:
532 if cwd:
517 os.chdir(cwd[-1])
533 os.chdir(cwd[-1])
518
534
519 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
535 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
520 path, lui = _getlocal(ui, rpath)
536 path, lui = _getlocal(ui, rpath)
521
537
522 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
538 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
523 # reposetup. Programs like TortoiseHg will call _dispatch several
539 # reposetup. Programs like TortoiseHg will call _dispatch several
524 # times so we keep track of configured extensions in _loaded.
540 # times so we keep track of configured extensions in _loaded.
525 extensions.loadall(lui)
541 extensions.loadall(lui)
526 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
542 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
527 # Propagate any changes to lui.__class__ by extensions
543 # Propagate any changes to lui.__class__ by extensions
528 ui.__class__ = lui.__class__
544 ui.__class__ = lui.__class__
529
545
530 # (uisetup and extsetup are handled in extensions.loadall)
546 # (uisetup and extsetup are handled in extensions.loadall)
531
547
532 for name, module in exts:
548 for name, module in exts:
533 cmdtable = getattr(module, 'cmdtable', {})
549 cmdtable = getattr(module, 'cmdtable', {})
534 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
550 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
535 if overrides:
551 if overrides:
536 ui.warn(_("extension '%s' overrides commands: %s\n")
552 ui.warn(_("extension '%s' overrides commands: %s\n")
537 % (name, " ".join(overrides)))
553 % (name, " ".join(overrides)))
538 commands.table.update(cmdtable)
554 commands.table.update(cmdtable)
539 _loaded.add(name)
555 _loaded.add(name)
540
556
541 # (reposetup is handled in hg.repository)
557 # (reposetup is handled in hg.repository)
542
558
543 addaliases(lui, commands.table)
559 addaliases(lui, commands.table)
544
560
545 # check for fallback encoding
561 # check for fallback encoding
546 fallback = lui.config('ui', 'fallbackencoding')
562 fallback = lui.config('ui', 'fallbackencoding')
547 if fallback:
563 if fallback:
548 encoding.fallbackencoding = fallback
564 encoding.fallbackencoding = fallback
549
565
550 fullargs = args
566 fullargs = args
551 cmd, func, args, options, cmdoptions = _parse(lui, args)
567 cmd, func, args, options, cmdoptions = _parse(lui, args)
552
568
553 if options["config"]:
569 if options["config"]:
554 raise util.Abort(_("option --config may not be abbreviated!"))
570 raise util.Abort(_("option --config may not be abbreviated!"))
555 if options["cwd"]:
571 if options["cwd"]:
556 raise util.Abort(_("option --cwd may not be abbreviated!"))
572 raise util.Abort(_("option --cwd may not be abbreviated!"))
557 if options["repository"]:
573 if options["repository"]:
558 raise util.Abort(_(
574 raise util.Abort(_(
559 "Option -R has to be separated from other options (e.g. not -qR) "
575 "Option -R has to be separated from other options (e.g. not -qR) "
560 "and --repository may only be abbreviated as --repo!"))
576 "and --repository may only be abbreviated as --repo!"))
561
577
562 if options["encoding"]:
578 if options["encoding"]:
563 encoding.encoding = options["encoding"]
579 encoding.encoding = options["encoding"]
564 if options["encodingmode"]:
580 if options["encodingmode"]:
565 encoding.encodingmode = options["encodingmode"]
581 encoding.encodingmode = options["encodingmode"]
566 if options["time"]:
582 if options["time"]:
567 def get_times():
583 def get_times():
568 t = os.times()
584 t = os.times()
569 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
585 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
570 t = (t[0], t[1], t[2], t[3], time.clock())
586 t = (t[0], t[1], t[2], t[3], time.clock())
571 return t
587 return t
572 s = get_times()
588 s = get_times()
573 def print_time():
589 def print_time():
574 t = get_times()
590 t = get_times()
575 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
591 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
576 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
592 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
577 atexit.register(print_time)
593 atexit.register(print_time)
578
594
579 if options['verbose'] or options['debug'] or options['quiet']:
595 if options['verbose'] or options['debug'] or options['quiet']:
580 for ui in (ui, lui):
596 for ui in (ui, lui):
581 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
597 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
582 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
598 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
583 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
599 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
584 if options['traceback']:
600 if options['traceback']:
585 for ui in (ui, lui):
601 for ui in (ui, lui):
586 ui.setconfig('ui', 'traceback', 'on')
602 ui.setconfig('ui', 'traceback', 'on')
587 if options['noninteractive']:
603 if options['noninteractive']:
588 for ui in (ui, lui):
604 for ui in (ui, lui):
589 ui.setconfig('ui', 'interactive', 'off')
605 ui.setconfig('ui', 'interactive', 'off')
590
606
591 if cmdoptions.get('insecure', False):
607 if cmdoptions.get('insecure', False):
592 for ui in (ui, lui):
608 for ui in (ui, lui):
593 ui.setconfig('web', 'cacerts', '')
609 ui.setconfig('web', 'cacerts', '')
594
610
595 if options['help']:
611 if options['help']:
596 return commands.help_(ui, cmd, options['version'])
612 return commands.help_(ui, cmd, options['version'])
597 elif options['version']:
613 elif options['version']:
598 return commands.version_(ui)
614 return commands.version_(ui)
599 elif not cmd:
615 elif not cmd:
600 return commands.help_(ui, 'shortlist')
616 return commands.help_(ui, 'shortlist')
601
617
602 repo = None
618 repo = None
603 cmdpats = args[:]
619 cmdpats = args[:]
604 if cmd not in commands.norepo.split():
620 if cmd not in commands.norepo.split():
605 # use the repo from the request only if we don't have -R
621 # use the repo from the request only if we don't have -R
606 if not rpath:
622 if not rpath:
607 repo = req.repo
623 repo = req.repo
608
624
609 if not repo:
625 if not repo:
610 try:
626 try:
611 repo = hg.repository(ui, path=path)
627 repo = hg.repository(ui, path=path)
612 ui = repo.ui
628 ui = repo.ui
613 if not repo.local():
629 if not repo.local():
614 raise util.Abort(_("repository '%s' is not local") % path)
630 raise util.Abort(_("repository '%s' is not local") % path)
615 ui.setconfig("bundle", "mainreporoot", repo.root)
631 ui.setconfig("bundle", "mainreporoot", repo.root)
616 except error.RequirementError:
632 except error.RequirementError:
617 raise
633 raise
618 except error.RepoError:
634 except error.RepoError:
619 if cmd not in commands.optionalrepo.split():
635 if cmd not in commands.optionalrepo.split():
620 if args and not path: # try to infer -R from command args
636 if args and not path: # try to infer -R from command args
621 repos = map(cmdutil.findrepo, args)
637 repos = map(cmdutil.findrepo, args)
622 guess = repos[0]
638 guess = repos[0]
623 if guess and repos.count(guess) == len(repos):
639 if guess and repos.count(guess) == len(repos):
624 req.args = ['--repository', guess] + fullargs
640 req.args = ['--repository', guess] + fullargs
625 return _dispatch(req)
641 return _dispatch(req)
626 if not path:
642 if not path:
627 raise error.RepoError(_("no repository found in %r"
643 raise error.RepoError(_("no repository found in %r"
628 " (.hg not found)") % os.getcwd())
644 " (.hg not found)") % os.getcwd())
629 raise
645 raise
630 args.insert(0, repo)
646 args.insert(0, repo)
631 elif rpath:
647 elif rpath:
632 ui.warn(_("warning: --repository ignored\n"))
648 ui.warn(_("warning: --repository ignored\n"))
633
649
634 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
650 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
635 ui.log("command", msg + "\n")
651 ui.log("command", msg + "\n")
636 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
652 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
637 try:
653 try:
638 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
654 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
639 cmdpats, cmdoptions)
655 cmdpats, cmdoptions)
640 finally:
656 finally:
641 if repo:
657 if repo:
642 repo.close()
658 repo.close()
643
659
644 def _runcommand(ui, options, cmd, cmdfunc):
660 def _runcommand(ui, options, cmd, cmdfunc):
645 def checkargs():
661 def checkargs():
646 try:
662 try:
647 return cmdfunc()
663 return cmdfunc()
648 except error.SignatureError:
664 except error.SignatureError:
649 raise error.CommandError(cmd, _("invalid arguments"))
665 raise error.CommandError(cmd, _("invalid arguments"))
650
666
651 if options['profile']:
667 if options['profile']:
652 format = ui.config('profiling', 'format', default='text')
668 format = ui.config('profiling', 'format', default='text')
653
669
654 if not format in ['text', 'kcachegrind']:
670 if not format in ['text', 'kcachegrind']:
655 ui.warn(_("unrecognized profiling format '%s'"
671 ui.warn(_("unrecognized profiling format '%s'"
656 " - Ignored\n") % format)
672 " - Ignored\n") % format)
657 format = 'text'
673 format = 'text'
658
674
659 output = ui.config('profiling', 'output')
675 output = ui.config('profiling', 'output')
660
676
661 if output:
677 if output:
662 path = ui.expandpath(output)
678 path = ui.expandpath(output)
663 ostream = open(path, 'wb')
679 ostream = open(path, 'wb')
664 else:
680 else:
665 ostream = sys.stderr
681 ostream = sys.stderr
666
682
667 try:
683 try:
668 from mercurial import lsprof
684 from mercurial import lsprof
669 except ImportError:
685 except ImportError:
670 raise util.Abort(_(
686 raise util.Abort(_(
671 'lsprof not available - install from '
687 'lsprof not available - install from '
672 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
688 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
673 p = lsprof.Profiler()
689 p = lsprof.Profiler()
674 p.enable(subcalls=True)
690 p.enable(subcalls=True)
675 try:
691 try:
676 return checkargs()
692 return checkargs()
677 finally:
693 finally:
678 p.disable()
694 p.disable()
679
695
680 if format == 'kcachegrind':
696 if format == 'kcachegrind':
681 import lsprofcalltree
697 import lsprofcalltree
682 calltree = lsprofcalltree.KCacheGrind(p)
698 calltree = lsprofcalltree.KCacheGrind(p)
683 calltree.output(ostream)
699 calltree.output(ostream)
684 else:
700 else:
685 # format == 'text'
701 # format == 'text'
686 stats = lsprof.Stats(p.getstats())
702 stats = lsprof.Stats(p.getstats())
687 stats.sort()
703 stats.sort()
688 stats.pprint(top=10, file=ostream, climit=5)
704 stats.pprint(top=10, file=ostream, climit=5)
689
705
690 if output:
706 if output:
691 ostream.close()
707 ostream.close()
692 else:
708 else:
693 return checkargs()
709 return checkargs()
General Comments 0
You need to be logged in to leave comments. Login now