##// END OF EJS Templates
ui: refactor option setting...
Matt Mackall -
r8136:6b5522cb default
parent child Browse files
Show More
@@ -1,424 +1,432 b''
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as _ui
13 13
14 14 def run():
15 15 "run the command in sys.argv"
16 16 sys.exit(dispatch(sys.argv[1:]))
17 17
18 18 def dispatch(args):
19 19 "run the command specified in args"
20 20 try:
21 u = _ui.ui(traceback='--traceback' in args)
21 u = _ui.ui()
22 if '--traceback' in args:
23 u.setconfig('ui', 'traceback', 'on')
22 24 except util.Abort, inst:
23 25 sys.stderr.write(_("abort: %s\n") % inst)
24 26 return -1
25 27 return _runcatch(u, args)
26 28
27 29 def _runcatch(ui, args):
28 30 def catchterm(*args):
29 31 raise error.SignalInterrupt
30 32
31 33 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
32 34 num = getattr(signal, name, None)
33 35 if num: signal.signal(num, catchterm)
34 36
35 37 try:
36 38 try:
37 39 # enter the debugger before command execution
38 40 if '--debugger' in args:
39 41 pdb.set_trace()
40 42 try:
41 43 return _dispatch(ui, args)
42 44 finally:
43 45 ui.flush()
44 46 except:
45 47 # enter the debugger when we hit an exception
46 48 if '--debugger' in args:
47 49 pdb.post_mortem(sys.exc_info()[2])
48 50 ui.print_exc()
49 51 raise
50 52
51 53 # Global exception handling, alphabetically
52 54 # Mercurial-specific first, followed by built-in and library exceptions
53 55 except error.AmbiguousCommand, inst:
54 56 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
55 57 (inst.args[0], " ".join(inst.args[1])))
56 58 except error.LockHeld, inst:
57 59 if inst.errno == errno.ETIMEDOUT:
58 60 reason = _('timed out waiting for lock held by %s') % inst.locker
59 61 else:
60 62 reason = _('lock held by %s') % inst.locker
61 63 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
62 64 except error.LockUnavailable, inst:
63 65 ui.warn(_("abort: could not lock %s: %s\n") %
64 66 (inst.desc or inst.filename, inst.strerror))
65 67 except error.ParseError, inst:
66 68 if inst.args[0]:
67 69 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
68 70 commands.help_(ui, inst.args[0])
69 71 else:
70 72 ui.warn(_("hg: %s\n") % inst.args[1])
71 73 commands.help_(ui, 'shortlist')
72 74 except error.RepoError, inst:
73 75 ui.warn(_("abort: %s!\n") % inst)
74 76 except error.ResponseError, inst:
75 77 ui.warn(_("abort: %s") % inst.args[0])
76 78 if not isinstance(inst.args[1], basestring):
77 79 ui.warn(" %r\n" % (inst.args[1],))
78 80 elif not inst.args[1]:
79 81 ui.warn(_(" empty string\n"))
80 82 else:
81 83 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
82 84 except error.RevlogError, inst:
83 85 ui.warn(_("abort: %s!\n") % inst)
84 86 except error.SignalInterrupt:
85 87 ui.warn(_("killed!\n"))
86 88 except error.UnknownCommand, inst:
87 89 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
88 90 commands.help_(ui, 'shortlist')
89 91 except util.Abort, inst:
90 92 ui.warn(_("abort: %s\n") % inst)
91 93 except ImportError, inst:
92 94 m = str(inst).split()[-1]
93 95 ui.warn(_("abort: could not import module %s!\n") % m)
94 96 if m in "mpatch bdiff".split():
95 97 ui.warn(_("(did you forget to compile extensions?)\n"))
96 98 elif m in "zlib".split():
97 99 ui.warn(_("(is your Python install correct?)\n"))
98 100 except IOError, inst:
99 101 if hasattr(inst, "code"):
100 102 ui.warn(_("abort: %s\n") % inst)
101 103 elif hasattr(inst, "reason"):
102 104 try: # usually it is in the form (errno, strerror)
103 105 reason = inst.reason.args[1]
104 106 except: # it might be anything, for example a string
105 107 reason = inst.reason
106 108 ui.warn(_("abort: error: %s\n") % reason)
107 109 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
108 110 if ui.debugflag:
109 111 ui.warn(_("broken pipe\n"))
110 112 elif getattr(inst, "strerror", None):
111 113 if getattr(inst, "filename", None):
112 114 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
113 115 else:
114 116 ui.warn(_("abort: %s\n") % inst.strerror)
115 117 else:
116 118 raise
117 119 except OSError, inst:
118 120 if getattr(inst, "filename", None):
119 121 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
120 122 else:
121 123 ui.warn(_("abort: %s\n") % inst.strerror)
122 124 except KeyboardInterrupt:
123 125 try:
124 126 ui.warn(_("interrupted!\n"))
125 127 except IOError, inst:
126 128 if inst.errno == errno.EPIPE:
127 129 if ui.debugflag:
128 130 ui.warn(_("\nbroken pipe\n"))
129 131 else:
130 132 raise
131 133 except MemoryError:
132 134 ui.warn(_("abort: out of memory\n"))
133 135 except SystemExit, inst:
134 136 # Commands shouldn't sys.exit directly, but give a return code.
135 137 # Just in case catch this and and pass exit code to caller.
136 138 return inst.code
137 139 except socket.error, inst:
138 140 ui.warn(_("abort: %s\n") % inst.args[-1])
139 141 except:
140 142 ui.warn(_("** unknown exception encountered, details follow\n"))
141 143 ui.warn(_("** report bug details to "
142 144 "http://www.selenic.com/mercurial/bts\n"))
143 145 ui.warn(_("** or mercurial@selenic.com\n"))
144 146 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
145 147 % util.version())
146 148 ui.warn(_("** Extensions loaded: %s\n")
147 149 % ", ".join([x[0] for x in extensions.extensions()]))
148 150 raise
149 151
150 152 return -1
151 153
152 154 def _findrepo(p):
153 155 while not os.path.isdir(os.path.join(p, ".hg")):
154 156 oldp, p = p, os.path.dirname(p)
155 157 if p == oldp:
156 158 return None
157 159
158 160 return p
159 161
160 162 def _parse(ui, args):
161 163 options = {}
162 164 cmdoptions = {}
163 165
164 166 try:
165 167 args = fancyopts.fancyopts(args, commands.globalopts, options)
166 168 except fancyopts.getopt.GetoptError, inst:
167 169 raise error.ParseError(None, inst)
168 170
169 171 if args:
170 172 cmd, args = args[0], args[1:]
171 173 aliases, i = cmdutil.findcmd(cmd, commands.table,
172 174 ui.config("ui", "strict"))
173 175 cmd = aliases[0]
174 176 defaults = ui.config("defaults", cmd)
175 177 if defaults:
176 178 args = shlex.split(defaults) + args
177 179 c = list(i[1])
178 180 else:
179 181 cmd = None
180 182 c = []
181 183
182 184 # combine global options into local
183 185 for o in commands.globalopts:
184 186 c.append((o[0], o[1], options[o[1]], o[3]))
185 187
186 188 try:
187 189 args = fancyopts.fancyopts(args, c, cmdoptions, True)
188 190 except fancyopts.getopt.GetoptError, inst:
189 191 raise error.ParseError(cmd, inst)
190 192
191 193 # separate global options back out
192 194 for o in commands.globalopts:
193 195 n = o[1]
194 196 options[n] = cmdoptions[n]
195 197 del cmdoptions[n]
196 198
197 199 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
198 200
199 201 def _parseconfig(config):
200 202 """parse the --config options from the command line"""
201 203 parsed = []
202 204 for cfg in config:
203 205 try:
204 206 name, value = cfg.split('=', 1)
205 207 section, name = name.split('.', 1)
206 208 if not section or not name:
207 209 raise IndexError
208 210 parsed.append((section, name, value))
209 211 except (IndexError, ValueError):
210 212 raise util.Abort(_('malformed --config option: %s') % cfg)
211 213 return parsed
212 214
213 215 def _earlygetopt(aliases, args):
214 216 """Return list of values for an option (or aliases).
215 217
216 218 The values are listed in the order they appear in args.
217 219 The options and values are removed from args.
218 220 """
219 221 try:
220 222 argcount = args.index("--")
221 223 except ValueError:
222 224 argcount = len(args)
223 225 shortopts = [opt for opt in aliases if len(opt) == 2]
224 226 values = []
225 227 pos = 0
226 228 while pos < argcount:
227 229 if args[pos] in aliases:
228 230 if pos + 1 >= argcount:
229 231 # ignore and let getopt report an error if there is no value
230 232 break
231 233 del args[pos]
232 234 values.append(args.pop(pos))
233 235 argcount -= 2
234 236 elif args[pos][:2] in shortopts:
235 237 # short option can have no following space, e.g. hg log -Rfoo
236 238 values.append(args.pop(pos)[2:])
237 239 argcount -= 1
238 240 else:
239 241 pos += 1
240 242 return values
241 243
242 244 def runcommand(lui, repo, cmd, fullargs, ui, options, d):
243 245 # run pre-hook, and abort if it fails
244 246 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
245 247 if ret:
246 248 return ret
247 249 ret = _runcommand(ui, options, cmd, d)
248 250 # run post-hook, passing command result
249 251 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
250 252 result = ret)
251 253 return ret
252 254
253 255 _loaded = {}
254 256 def _dispatch(ui, args):
255 257 # read --config before doing anything else
256 258 # (e.g. to change trust settings for reading .hg/hgrc)
257 259 config = _earlygetopt(['--config'], args)
258 260 if config:
259 ui.updateopts(config=_parseconfig(config))
261 ui.updateopts(_parseconfig(config))
260 262
261 263 # check for cwd
262 264 cwd = _earlygetopt(['--cwd'], args)
263 265 if cwd:
264 266 os.chdir(cwd[-1])
265 267
266 268 # read the local repository .hgrc into a local ui object
267 269 path = _findrepo(os.getcwd()) or ""
268 270 if not path:
269 271 lui = ui
270 272 if path:
271 273 try:
272 274 lui = _ui.ui(parentui=ui)
273 275 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
274 276 except IOError:
275 277 pass
276 278
277 279 # now we can expand paths, even ones in .hg/hgrc
278 280 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
279 281 if rpath:
280 282 path = lui.expandpath(rpath[-1])
281 283 lui = _ui.ui(parentui=ui)
282 284 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
283 285
284 286 extensions.loadall(lui)
285 287 for name, module in extensions.extensions():
286 288 if name in _loaded:
287 289 continue
288 290
289 291 # setup extensions
290 292 # TODO this should be generalized to scheme, where extensions can
291 293 # redepend on other extensions. then we should toposort them, and
292 294 # do initialization in correct order
293 295 extsetup = getattr(module, 'extsetup', None)
294 296 if extsetup:
295 297 extsetup()
296 298
297 299 cmdtable = getattr(module, 'cmdtable', {})
298 300 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
299 301 if overrides:
300 302 ui.warn(_("extension '%s' overrides commands: %s\n")
301 303 % (name, " ".join(overrides)))
302 304 commands.table.update(cmdtable)
303 305 _loaded[name] = 1
304 306 # check for fallback encoding
305 307 fallback = lui.config('ui', 'fallbackencoding')
306 308 if fallback:
307 309 encoding.fallbackencoding = fallback
308 310
309 311 fullargs = args
310 312 cmd, func, args, options, cmdoptions = _parse(lui, args)
311 313
312 314 if options["config"]:
313 315 raise util.Abort(_("Option --config may not be abbreviated!"))
314 316 if options["cwd"]:
315 317 raise util.Abort(_("Option --cwd may not be abbreviated!"))
316 318 if options["repository"]:
317 319 raise util.Abort(_(
318 320 "Option -R has to be separated from other options (i.e. not -qR) "
319 321 "and --repository may only be abbreviated as --repo!"))
320 322
321 323 if options["encoding"]:
322 324 encoding.encoding = options["encoding"]
323 325 if options["encodingmode"]:
324 326 encoding.encodingmode = options["encodingmode"]
325 327 if options["time"]:
326 328 def get_times():
327 329 t = os.times()
328 330 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
329 331 t = (t[0], t[1], t[2], t[3], time.clock())
330 332 return t
331 333 s = get_times()
332 334 def print_time():
333 335 t = get_times()
334 336 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
335 337 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
336 338 atexit.register(print_time)
337 339
338 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
339 not options["noninteractive"], options["traceback"])
340 if options['verbose'] or options['debug'] or options['quiet']:
341 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
342 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
343 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
344 if options['traceback']:
345 ui.setconfig('ui', 'traceback', 'on')
346 if options['noninteractive']:
347 ui.setconfig('ui', 'interactive', 'off')
340 348
341 349 if options['help']:
342 350 return commands.help_(ui, cmd, options['version'])
343 351 elif options['version']:
344 352 return commands.version_(ui)
345 353 elif not cmd:
346 354 return commands.help_(ui, 'shortlist')
347 355
348 356 repo = None
349 357 if cmd not in commands.norepo.split():
350 358 try:
351 359 repo = hg.repository(ui, path=path)
352 360 ui = repo.ui
353 361 if not repo.local():
354 362 raise util.Abort(_("repository '%s' is not local") % path)
355 363 ui.setconfig("bundle", "mainreporoot", repo.root)
356 364 except error.RepoError:
357 365 if cmd not in commands.optionalrepo.split():
358 366 if args and not path: # try to infer -R from command args
359 367 repos = map(_findrepo, args)
360 368 guess = repos[0]
361 369 if guess and repos.count(guess) == len(repos):
362 370 return _dispatch(ui, ['--repository', guess] + fullargs)
363 371 if not path:
364 372 raise error.RepoError(_("There is no Mercurial repository"
365 373 " here (.hg not found)"))
366 374 raise
367 375 args.insert(0, repo)
368 376 elif rpath:
369 377 ui.warn("warning: --repository ignored\n")
370 378
371 379 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
372 380 return runcommand(lui, repo, cmd, fullargs, ui, options, d)
373 381
374 382 def _runcommand(ui, options, cmd, cmdfunc):
375 383 def checkargs():
376 384 try:
377 385 return cmdfunc()
378 386 except error.SignatureError:
379 387 raise error.ParseError(cmd, _("invalid arguments"))
380 388
381 389 if options['profile']:
382 390 format = ui.config('profiling', 'format', default='text')
383 391
384 392 if not format in ['text', 'kcachegrind']:
385 393 ui.warn(_("unrecognized profiling format '%s'"
386 394 " - Ignored\n") % format)
387 395 format = 'text'
388 396
389 397 output = ui.config('profiling', 'output')
390 398
391 399 if output:
392 400 path = os.path.expanduser(output)
393 401 path = ui.expandpath(path)
394 402 ostream = open(path, 'wb')
395 403 else:
396 404 ostream = sys.stderr
397 405
398 406 try:
399 407 from mercurial import lsprof
400 408 except ImportError:
401 409 raise util.Abort(_(
402 410 'lsprof not available - install from '
403 411 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
404 412 p = lsprof.Profiler()
405 413 p.enable(subcalls=True)
406 414 try:
407 415 return checkargs()
408 416 finally:
409 417 p.disable()
410 418
411 419 if format == 'kcachegrind':
412 420 import lsprofcalltree
413 421 calltree = lsprofcalltree.KCacheGrind(p)
414 422 calltree.output(ostream)
415 423 else:
416 424 # format == 'text'
417 425 stats = lsprof.Stats(p.getstats())
418 426 stats.sort()
419 427 stats.pprint(top=10, file=ostream, climit=5)
420 428
421 429 if output:
422 430 ostream.close()
423 431 else:
424 432 return checkargs()
@@ -1,310 +1,312 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os
10 10 from mercurial import ui, hg, util, hook, error, encoding
11 11 from mercurial import templater, templatefilters
12 12 from common import get_mtime, ErrorResponse
13 13 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 14 from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED
15 15 from request import wsgirequest
16 16 import webcommands, protocol, webutil
17 17
18 18 perms = {
19 19 'changegroup': 'pull',
20 20 'changegroupsubset': 'pull',
21 21 'unbundle': 'push',
22 22 'stream_out': 'pull',
23 23 }
24 24
25 25 class hgweb(object):
26 26 def __init__(self, repo, name=None):
27 27 if isinstance(repo, str):
28 parentui = ui.ui(report_untrusted=False, interactive=False)
28 parentui = ui.ui()
29 parentui.setconfig('ui', 'report_untrusted', 'off')
30 parentui.setconfig('ui', 'interactive', 'off')
29 31 self.repo = hg.repository(parentui, repo)
30 32 else:
31 33 self.repo = repo
32 34
33 35 hook.redirect(True)
34 36 self.mtime = -1
35 37 self.reponame = name
36 38 self.archives = 'zip', 'gz', 'bz2'
37 39 self.stripecount = 1
38 40 # a repo owner may set web.templates in .hg/hgrc to get any file
39 41 # readable by the user running the CGI script
40 42 self.templatepath = self.config('web', 'templates')
41 43
42 44 # The CGI scripts are often run by a user different from the repo owner.
43 45 # Trust the settings from the .hg/hgrc files by default.
44 46 def config(self, section, name, default=None, untrusted=True):
45 47 return self.repo.ui.config(section, name, default,
46 48 untrusted=untrusted)
47 49
48 50 def configbool(self, section, name, default=False, untrusted=True):
49 51 return self.repo.ui.configbool(section, name, default,
50 52 untrusted=untrusted)
51 53
52 54 def configlist(self, section, name, default=None, untrusted=True):
53 55 return self.repo.ui.configlist(section, name, default,
54 56 untrusted=untrusted)
55 57
56 58 def refresh(self):
57 59 mtime = get_mtime(self.repo.root)
58 60 if mtime != self.mtime:
59 61 self.mtime = mtime
60 62 self.repo = hg.repository(self.repo.ui, self.repo.root)
61 63 self.maxchanges = int(self.config("web", "maxchanges", 10))
62 64 self.stripecount = int(self.config("web", "stripes", 1))
63 65 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
64 66 self.maxfiles = int(self.config("web", "maxfiles", 10))
65 67 self.allowpull = self.configbool("web", "allowpull", True)
66 68 self.encoding = self.config("web", "encoding", encoding.encoding)
67 69
68 70 def run(self):
69 71 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
70 72 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
71 73 import mercurial.hgweb.wsgicgi as wsgicgi
72 74 wsgicgi.launch(self)
73 75
74 76 def __call__(self, env, respond):
75 77 req = wsgirequest(env, respond)
76 78 return self.run_wsgi(req)
77 79
78 80 def run_wsgi(self, req):
79 81
80 82 self.refresh()
81 83
82 84 # process this if it's a protocol request
83 85 # protocol bits don't need to create any URLs
84 86 # and the clients always use the old URL structure
85 87
86 88 cmd = req.form.get('cmd', [''])[0]
87 89 if cmd and cmd in protocol.__all__:
88 90 try:
89 91 if cmd in perms:
90 92 try:
91 93 self.check_perm(req, perms[cmd])
92 94 except ErrorResponse, inst:
93 95 if cmd == 'unbundle':
94 96 req.drain()
95 97 raise
96 98 method = getattr(protocol, cmd)
97 99 return method(self.repo, req)
98 100 except ErrorResponse, inst:
99 101 req.respond(inst, protocol.HGTYPE)
100 102 if not inst.message:
101 103 return []
102 104 return '0\n%s\n' % inst.message,
103 105
104 106 # work with CGI variables to create coherent structure
105 107 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
106 108
107 109 req.url = req.env['SCRIPT_NAME']
108 110 if not req.url.endswith('/'):
109 111 req.url += '/'
110 112 if 'REPO_NAME' in req.env:
111 113 req.url += req.env['REPO_NAME'] + '/'
112 114
113 115 if 'PATH_INFO' in req.env:
114 116 parts = req.env['PATH_INFO'].strip('/').split('/')
115 117 repo_parts = req.env.get('REPO_NAME', '').split('/')
116 118 if parts[:len(repo_parts)] == repo_parts:
117 119 parts = parts[len(repo_parts):]
118 120 query = '/'.join(parts)
119 121 else:
120 122 query = req.env['QUERY_STRING'].split('&', 1)[0]
121 123 query = query.split(';', 1)[0]
122 124
123 125 # translate user-visible url structure to internal structure
124 126
125 127 args = query.split('/', 2)
126 128 if 'cmd' not in req.form and args and args[0]:
127 129
128 130 cmd = args.pop(0)
129 131 style = cmd.rfind('-')
130 132 if style != -1:
131 133 req.form['style'] = [cmd[:style]]
132 134 cmd = cmd[style+1:]
133 135
134 136 # avoid accepting e.g. style parameter as command
135 137 if hasattr(webcommands, cmd):
136 138 req.form['cmd'] = [cmd]
137 139 else:
138 140 cmd = ''
139 141
140 142 if cmd == 'static':
141 143 req.form['file'] = ['/'.join(args)]
142 144 else:
143 145 if args and args[0]:
144 146 node = args.pop(0)
145 147 req.form['node'] = [node]
146 148 if args:
147 149 req.form['file'] = args
148 150
149 151 if cmd == 'archive':
150 152 fn = req.form['node'][0]
151 153 for type_, spec in self.archive_specs.iteritems():
152 154 ext = spec[2]
153 155 if fn.endswith(ext):
154 156 req.form['node'] = [fn[:-len(ext)]]
155 157 req.form['type'] = [type_]
156 158
157 159 # process the web interface request
158 160
159 161 try:
160 162 tmpl = self.templater(req)
161 163 ctype = tmpl('mimetype', encoding=self.encoding)
162 164 ctype = templater.stringify(ctype)
163 165
164 166 # check read permissions non-static content
165 167 if cmd != 'static':
166 168 self.check_perm(req, None)
167 169
168 170 if cmd == '':
169 171 req.form['cmd'] = [tmpl.cache['default']]
170 172 cmd = req.form['cmd'][0]
171 173
172 174 if cmd not in webcommands.__all__:
173 175 msg = 'no such method: %s' % cmd
174 176 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
175 177 elif cmd == 'file' and 'raw' in req.form.get('style', []):
176 178 self.ctype = ctype
177 179 content = webcommands.rawfile(self, req, tmpl)
178 180 else:
179 181 content = getattr(webcommands, cmd)(self, req, tmpl)
180 182 req.respond(HTTP_OK, ctype)
181 183
182 184 return content
183 185
184 186 except error.LookupError, err:
185 187 req.respond(HTTP_NOT_FOUND, ctype)
186 188 msg = str(err)
187 189 if 'manifest' not in msg:
188 190 msg = 'revision not found: %s' % err.name
189 191 return tmpl('error', error=msg)
190 192 except (error.RepoError, error.RevlogError), inst:
191 193 req.respond(HTTP_SERVER_ERROR, ctype)
192 194 return tmpl('error', error=str(inst))
193 195 except ErrorResponse, inst:
194 196 req.respond(inst, ctype)
195 197 return tmpl('error', error=inst.message)
196 198
197 199 def templater(self, req):
198 200
199 201 # determine scheme, port and server name
200 202 # this is needed to create absolute urls
201 203
202 204 proto = req.env.get('wsgi.url_scheme')
203 205 if proto == 'https':
204 206 proto = 'https'
205 207 default_port = "443"
206 208 else:
207 209 proto = 'http'
208 210 default_port = "80"
209 211
210 212 port = req.env["SERVER_PORT"]
211 213 port = port != default_port and (":" + port) or ""
212 214 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
213 215 staticurl = self.config("web", "staticurl") or req.url + 'static/'
214 216 if not staticurl.endswith('/'):
215 217 staticurl += '/'
216 218
217 219 # some functions for the templater
218 220
219 221 def header(**map):
220 222 yield tmpl('header', encoding=self.encoding, **map)
221 223
222 224 def footer(**map):
223 225 yield tmpl("footer", **map)
224 226
225 227 def motd(**map):
226 228 yield self.config("web", "motd", "")
227 229
228 230 # figure out which style to use
229 231
230 232 vars = {}
231 233 style = self.config("web", "style", "paper")
232 234 if 'style' in req.form:
233 235 style = req.form['style'][0]
234 236 vars['style'] = style
235 237
236 238 start = req.url[-1] == '?' and '&' or '?'
237 239 sessionvars = webutil.sessionvars(vars, start)
238 240 mapfile = templater.stylemap(style, self.templatepath)
239 241
240 242 if not self.reponame:
241 243 self.reponame = (self.config("web", "name")
242 244 or req.env.get('REPO_NAME')
243 245 or req.url.strip('/') or self.repo.root)
244 246
245 247 # create the templater
246 248
247 249 tmpl = templater.templater(mapfile, templatefilters.filters,
248 250 defaults={"url": req.url,
249 251 "staticurl": staticurl,
250 252 "urlbase": urlbase,
251 253 "repo": self.reponame,
252 254 "header": header,
253 255 "footer": footer,
254 256 "motd": motd,
255 257 "sessionvars": sessionvars
256 258 })
257 259 return tmpl
258 260
259 261 def archivelist(self, nodeid):
260 262 allowed = self.configlist("web", "allow_archive")
261 263 for i, spec in self.archive_specs.iteritems():
262 264 if i in allowed or self.configbool("web", "allow" + i):
263 265 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
264 266
265 267 archive_specs = {
266 268 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
267 269 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
268 270 'zip': ('application/zip', 'zip', '.zip', None),
269 271 }
270 272
271 273 def check_perm(self, req, op):
272 274 '''Check permission for operation based on request data (including
273 275 authentication info). Return if op allowed, else raise an ErrorResponse
274 276 exception.'''
275 277
276 278 user = req.env.get('REMOTE_USER')
277 279
278 280 deny_read = self.configlist('web', 'deny_read')
279 281 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
280 282 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
281 283
282 284 allow_read = self.configlist('web', 'allow_read')
283 285 result = (not allow_read) or (allow_read == ['*'])
284 286 if not (result or user in allow_read):
285 287 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
286 288
287 289 if op == 'pull' and not self.allowpull:
288 290 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
289 291 elif op == 'pull' or op is None: # op is None for interface requests
290 292 return
291 293
292 294 # enforce that you can only push using POST requests
293 295 if req.env['REQUEST_METHOD'] != 'POST':
294 296 msg = 'push requires POST request'
295 297 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
296 298
297 299 # require ssl by default for pushing, auth info cannot be sniffed
298 300 # and replayed
299 301 scheme = req.env.get('wsgi.url_scheme')
300 302 if self.configbool('web', 'push_ssl', True) and scheme != 'https':
301 303 raise ErrorResponse(HTTP_OK, 'ssl required')
302 304
303 305 deny = self.configlist('web', 'deny_push')
304 306 if deny and (not user or deny == ['*'] or user in deny):
305 307 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
306 308
307 309 allow = self.configlist('web', 'allow_push')
308 310 result = allow and (allow == ['*'] or user in allow)
309 311 if not result:
310 312 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
@@ -1,327 +1,332 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os
10 10 from mercurial.i18n import _
11 11 from mercurial import ui, hg, util, templater, templatefilters, error, encoding
12 12 from common import ErrorResponse, get_mtime, staticfile, paritygen,\
13 13 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 14 from hgweb_mod import hgweb
15 15 from request import wsgirequest
16 16
17 17 # This is a stopgap
18 18 class hgwebdir(object):
19 19 def __init__(self, config, parentui=None):
20 20 def cleannames(items):
21 21 return [(util.pconvert(name).strip('/'), path)
22 22 for name, path in items]
23 23
24 self.parentui = parentui or ui.ui(report_untrusted=False,
25 interactive = False)
24 if parentui:
25 self.parentui = parentui
26 else:
27 self.parentui = ui.ui()
28 self.parentui.setconfig('ui', 'report_untrusted', 'off')
29 self.parentui.setconfig('ui', 'interactive', 'off')
30
26 31 self.motd = None
27 32 self.style = 'paper'
28 33 self.stripecount = None
29 34 self.repos_sorted = ('name', False)
30 35 self._baseurl = None
31 36 if isinstance(config, (list, tuple)):
32 37 self.repos = cleannames(config)
33 38 self.repos_sorted = ('', False)
34 39 elif isinstance(config, dict):
35 40 self.repos = util.sort(cleannames(config.items()))
36 41 else:
37 42 if isinstance(config, util.configparser):
38 43 cp = config
39 44 else:
40 45 cp = util.configparser()
41 46 cp.read(config)
42 47 self.repos = []
43 48 if cp.has_section('web'):
44 49 if cp.has_option('web', 'motd'):
45 50 self.motd = cp.get('web', 'motd')
46 51 if cp.has_option('web', 'style'):
47 52 self.style = cp.get('web', 'style')
48 53 if cp.has_option('web', 'stripes'):
49 54 self.stripecount = int(cp.get('web', 'stripes'))
50 55 if cp.has_option('web', 'baseurl'):
51 56 self._baseurl = cp.get('web', 'baseurl')
52 57 if cp.has_section('paths'):
53 58 paths = cleannames(cp.items('paths'))
54 59 for prefix, root in paths:
55 60 roothead, roottail = os.path.split(root)
56 61 # "foo = /bar/*" makes every subrepo of /bar/ to be
57 62 # mounted as foo/subrepo
58 63 # and "foo = /bar/**" does even recurse inside the
59 64 # subdirectories, remember to use it without working dir.
60 65 try:
61 66 recurse = {'*': False, '**': True}[roottail]
62 67 except KeyError:
63 68 self.repos.append((prefix, root))
64 69 continue
65 70 roothead = os.path.normpath(roothead)
66 71 for path in util.walkrepos(roothead, followsym=True,
67 72 recurse=recurse):
68 73 path = os.path.normpath(path)
69 74 name = util.pconvert(path[len(roothead):]).strip('/')
70 75 if prefix:
71 76 name = prefix + '/' + name
72 77 self.repos.append((name, path))
73 78 if cp.has_section('collections'):
74 79 for prefix, root in cp.items('collections'):
75 80 for path in util.walkrepos(root, followsym=True):
76 81 repo = os.path.normpath(path)
77 82 name = repo
78 83 if name.startswith(prefix):
79 84 name = name[len(prefix):]
80 85 self.repos.append((name.lstrip(os.sep), repo))
81 86 self.repos.sort()
82 87
83 88 def run(self):
84 89 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
85 90 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
86 91 import mercurial.hgweb.wsgicgi as wsgicgi
87 92 wsgicgi.launch(self)
88 93
89 94 def __call__(self, env, respond):
90 95 req = wsgirequest(env, respond)
91 96 return self.run_wsgi(req)
92 97
93 98 def read_allowed(self, ui, req):
94 99 """Check allow_read and deny_read config options of a repo's ui object
95 100 to determine user permissions. By default, with neither option set (or
96 101 both empty), allow all users to read the repo. There are two ways a
97 102 user can be denied read access: (1) deny_read is not empty, and the
98 103 user is unauthenticated or deny_read contains user (or *), and (2)
99 104 allow_read is not empty and the user is not in allow_read. Return True
100 105 if user is allowed to read the repo, else return False."""
101 106
102 107 user = req.env.get('REMOTE_USER')
103 108
104 109 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
105 110 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
106 111 return False
107 112
108 113 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
109 114 # by default, allow reading if no allow_read option has been set
110 115 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
111 116 return True
112 117
113 118 return False
114 119
115 120 def run_wsgi(self, req):
116 121
117 122 try:
118 123 try:
119 124
120 125 virtual = req.env.get("PATH_INFO", "").strip('/')
121 126 tmpl = self.templater(req)
122 127 ctype = tmpl('mimetype', encoding=encoding.encoding)
123 128 ctype = templater.stringify(ctype)
124 129
125 130 # a static file
126 131 if virtual.startswith('static/') or 'static' in req.form:
127 132 if virtual.startswith('static/'):
128 133 fname = virtual[7:]
129 134 else:
130 135 fname = req.form['static'][0]
131 136 static = templater.templatepath('static')
132 137 return (staticfile(static, fname, req),)
133 138
134 139 # top-level index
135 140 elif not virtual:
136 141 req.respond(HTTP_OK, ctype)
137 142 return self.makeindex(req, tmpl)
138 143
139 144 # nested indexes and hgwebs
140 145
141 146 repos = dict(self.repos)
142 147 while virtual:
143 148 real = repos.get(virtual)
144 149 if real:
145 150 req.env['REPO_NAME'] = virtual
146 151 try:
147 152 repo = hg.repository(self.parentui, real)
148 153 return hgweb(repo).run_wsgi(req)
149 154 except IOError, inst:
150 155 msg = inst.strerror
151 156 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
152 157 except error.RepoError, inst:
153 158 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
154 159
155 160 # browse subdirectories
156 161 subdir = virtual + '/'
157 162 if [r for r in repos if r.startswith(subdir)]:
158 163 req.respond(HTTP_OK, ctype)
159 164 return self.makeindex(req, tmpl, subdir)
160 165
161 166 up = virtual.rfind('/')
162 167 if up < 0:
163 168 break
164 169 virtual = virtual[:up]
165 170
166 171 # prefixes not found
167 172 req.respond(HTTP_NOT_FOUND, ctype)
168 173 return tmpl("notfound", repo=virtual)
169 174
170 175 except ErrorResponse, err:
171 176 req.respond(err, ctype)
172 177 return tmpl('error', error=err.message or '')
173 178 finally:
174 179 tmpl = None
175 180
176 181 def makeindex(self, req, tmpl, subdir=""):
177 182
178 183 def archivelist(ui, nodeid, url):
179 184 allowed = ui.configlist("web", "allow_archive", untrusted=True)
180 185 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
181 186 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
182 187 untrusted=True):
183 188 yield {"type" : i[0], "extension": i[1],
184 189 "node": nodeid, "url": url}
185 190
186 191 def entries(sortcolumn="", descending=False, subdir="", **map):
187 192 def sessionvars(**map):
188 193 fields = []
189 194 if 'style' in req.form:
190 195 style = req.form['style'][0]
191 196 if style != get('web', 'style', ''):
192 197 fields.append(('style', style))
193 198
194 199 separator = url[-1] == '?' and ';' or '?'
195 200 for name, value in fields:
196 201 yield dict(name=name, value=value, separator=separator)
197 202 separator = ';'
198 203
199 204 rows = []
200 205 parity = paritygen(self.stripecount)
201 206 for name, path in self.repos:
202 207 if not name.startswith(subdir):
203 208 continue
204 209 name = name[len(subdir):]
205 210
206 211 u = ui.ui(parentui=self.parentui)
207 212 try:
208 213 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
209 214 except Exception, e:
210 215 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
211 216 continue
212 217 def get(section, name, default=None):
213 218 return u.config(section, name, default, untrusted=True)
214 219
215 220 if u.configbool("web", "hidden", untrusted=True):
216 221 continue
217 222
218 223 if not self.read_allowed(u, req):
219 224 continue
220 225
221 226 parts = [name]
222 227 if 'PATH_INFO' in req.env:
223 228 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
224 229 if req.env['SCRIPT_NAME']:
225 230 parts.insert(0, req.env['SCRIPT_NAME'])
226 231 url = ('/'.join(parts).replace("//", "/")) + '/'
227 232
228 233 # update time with local timezone
229 234 try:
230 235 d = (get_mtime(path), util.makedate()[1])
231 236 except OSError:
232 237 continue
233 238
234 239 contact = get_contact(get)
235 240 description = get("web", "description", "")
236 241 name = get("web", "name", name)
237 242 row = dict(contact=contact or "unknown",
238 243 contact_sort=contact.upper() or "unknown",
239 244 name=name,
240 245 name_sort=name,
241 246 url=url,
242 247 description=description or "unknown",
243 248 description_sort=description.upper() or "unknown",
244 249 lastchange=d,
245 250 lastchange_sort=d[1]-d[0],
246 251 sessionvars=sessionvars,
247 252 archives=archivelist(u, "tip", url))
248 253 if (not sortcolumn
249 254 or (sortcolumn, descending) == self.repos_sorted):
250 255 # fast path for unsorted output
251 256 row['parity'] = parity.next()
252 257 yield row
253 258 else:
254 259 rows.append((row["%s_sort" % sortcolumn], row))
255 260 if rows:
256 261 rows.sort()
257 262 if descending:
258 263 rows.reverse()
259 264 for key, row in rows:
260 265 row['parity'] = parity.next()
261 266 yield row
262 267
263 268 sortable = ["name", "description", "contact", "lastchange"]
264 269 sortcolumn, descending = self.repos_sorted
265 270 if 'sort' in req.form:
266 271 sortcolumn = req.form['sort'][0]
267 272 descending = sortcolumn.startswith('-')
268 273 if descending:
269 274 sortcolumn = sortcolumn[1:]
270 275 if sortcolumn not in sortable:
271 276 sortcolumn = ""
272 277
273 278 sort = [("sort_%s" % column,
274 279 "%s%s" % ((not descending and column == sortcolumn)
275 280 and "-" or "", column))
276 281 for column in sortable]
277 282
278 283 if self._baseurl is not None:
279 284 req.env['SCRIPT_NAME'] = self._baseurl
280 285
281 286 return tmpl("index", entries=entries, subdir=subdir,
282 287 sortcolumn=sortcolumn, descending=descending,
283 288 **dict(sort))
284 289
285 290 def templater(self, req):
286 291
287 292 def header(**map):
288 293 yield tmpl('header', encoding=encoding.encoding, **map)
289 294
290 295 def footer(**map):
291 296 yield tmpl("footer", **map)
292 297
293 298 def motd(**map):
294 299 if self.motd is not None:
295 300 yield self.motd
296 301 else:
297 302 yield config('web', 'motd', '')
298 303
299 304 def config(section, name, default=None, untrusted=True):
300 305 return self.parentui.config(section, name, default, untrusted)
301 306
302 307 if self._baseurl is not None:
303 308 req.env['SCRIPT_NAME'] = self._baseurl
304 309
305 310 url = req.env.get('SCRIPT_NAME', '')
306 311 if not url.endswith('/'):
307 312 url += '/'
308 313
309 314 staticurl = config('web', 'staticurl') or url + 'static/'
310 315 if not staticurl.endswith('/'):
311 316 staticurl += '/'
312 317
313 318 style = self.style
314 319 if style is None:
315 320 style = config('web', 'style', '')
316 321 if 'style' in req.form:
317 322 style = req.form['style'][0]
318 323 if self.stripecount is None:
319 324 self.stripecount = int(config('web', 'stripes', 1))
320 325 mapfile = templater.stylemap(style)
321 326 tmpl = templater.templater(mapfile, templatefilters.filters,
322 327 defaults={"header": header,
323 328 "footer": footer,
324 329 "motd": motd,
325 330 "url": url,
326 331 "staticurl": staticurl})
327 332 return tmpl
@@ -1,478 +1,464 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9 import errno, getpass, os, re, socket, sys, tempfile
10 10 import ConfigParser, traceback, util
11 11
12 12 def dupconfig(orig):
13 13 new = util.configparser(orig.defaults())
14 14 updateconfig(orig, new)
15 15 return new
16 16
17 17 def updateconfig(source, dest, sections=None):
18 18 if not sections:
19 19 sections = source.sections()
20 20 for section in sections:
21 21 if not dest.has_section(section):
22 22 dest.add_section(section)
23 23 for name, value in source.items(section, raw=True):
24 24 dest.set(section, name, value)
25 25
26 26 class ui(object):
27 def __init__(self, verbose=False, debug=False, quiet=False,
28 interactive=True, traceback=False, report_untrusted=True,
29 parentui=None):
27 def __init__(self, parentui=None):
30 28 self.buffers = []
29 self.quiet = self.verbose = self.debugflag = self.traceback = False
30 self.interactive = self.report_untrusted = True
31
31 32 if parentui is None:
32 33 # this is the parent of all ui children
33 34 self.parentui = None
34 self.quiet = quiet
35 self.verbose = verbose
36 self.debugflag = debug
37 self.interactive = interactive
38 self.traceback = traceback
39 self.report_untrusted = report_untrusted
40 35 self.trusted_users = {}
41 36 self.trusted_groups = {}
42 37 self.overlay = util.configparser()
43 38 # if ucdata is not None, its keys must be a superset of cdata's
44 39 self.cdata = util.configparser()
45 40 self.ucdata = None
46 41 # we always trust global config files
47 42 self.readconfig(util.rcpath(), assumetrusted=True)
48 self.updateopts(verbose, debug, quiet, interactive)
49 43 else:
50 44 # parentui may point to an ui object which is already a child
51 45 self.parentui = parentui.parentui or parentui
52 46 self.trusted_users = parentui.trusted_users.copy()
53 47 self.trusted_groups = parentui.trusted_groups.copy()
54 48 self.cdata = dupconfig(self.parentui.cdata)
55 49 self.overlay = dupconfig(self.parentui.overlay)
56 50 if self.parentui.ucdata:
57 51 self.ucdata = dupconfig(self.parentui.ucdata)
58 52 if self.parentui is not parentui:
59 53 self.overlay = util.configparser()
60 54 updateconfig(parentui.overlay, self.overlay)
61 55 self.buffers = parentui.buffers
62 56
63 57 def __getattr__(self, key):
64 58 return getattr(self.parentui, key)
65 59
66 60 _isatty = None
67 61 def isatty(self):
68 62 if ui._isatty is None:
69 ui._isatty = sys.stdin.isatty()
63 try:
64 ui._isatty = sys.stdin.isatty()
65 except AttributeError: # not a real file object
66 ui._isatty = False
70 67 return ui._isatty
71 68
72 def updateopts(self, verbose=False, debug=False, quiet=False,
73 interactive=True, traceback=False, config=[]):
69 def updateopts(self, config):
74 70 for section, name, value in config:
75 71 self.setconfig(section, name, value)
76 72
77 if quiet or verbose or debug:
78 self.setconfig('ui', 'quiet', str(bool(quiet)))
79 self.setconfig('ui', 'verbose', str(bool(verbose)))
80 self.setconfig('ui', 'debug', str(bool(debug)))
81
82 if not interactive:
83 self.setconfig('ui', 'interactive', 'False')
84 self.interactive = False
85
86 self.traceback = self.traceback or traceback
87
88 73 def verbosity_constraints(self):
89 74 self.quiet = self.configbool('ui', 'quiet')
90 75 self.verbose = self.configbool('ui', 'verbose')
91 76 self.debugflag = self.configbool('ui', 'debug')
92 77
93 78 if self.debugflag:
94 79 self.verbose = True
95 80 self.quiet = False
96 81 elif self.verbose and self.quiet:
97 82 self.quiet = self.verbose = False
98 83
99 84 def _is_trusted(self, fp, f, warn=True):
100 85 st = util.fstat(fp)
101 86 if util.isowner(fp, st):
102 87 return True
103 88 tusers = self.trusted_users
104 89 tgroups = self.trusted_groups
105 90 if not tusers:
106 91 user = util.username()
107 92 if user is not None:
108 93 self.trusted_users[user] = 1
109 94 self.fixconfig(section='trusted')
110 95 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
111 96 user = util.username(st.st_uid)
112 97 group = util.groupname(st.st_gid)
113 98 if user not in tusers and group not in tgroups:
114 99 if warn and self.report_untrusted:
115 100 self.warn(_('Not trusting file %s from untrusted '
116 101 'user %s, group %s\n') % (f, user, group))
117 102 return False
118 103 return True
119 104
120 105 def readconfig(self, fn, root=None, assumetrusted=False):
121 106 if isinstance(fn, basestring):
122 107 fn = [fn]
123 108 for f in fn:
124 109 try:
125 110 fp = open(f)
126 111 except IOError:
127 112 continue
128 113 cdata = self.cdata
129 114 trusted = assumetrusted or self._is_trusted(fp, f)
130 115 if not trusted:
131 116 if self.ucdata is None:
132 117 self.ucdata = dupconfig(self.cdata)
133 118 cdata = self.ucdata
134 119 elif self.ucdata is not None:
135 120 # use a separate configparser, so that we don't accidentally
136 121 # override ucdata settings later on.
137 122 cdata = util.configparser()
138 123
139 124 try:
140 125 cdata.readfp(fp, f)
141 126 except ConfigParser.ParsingError, inst:
142 127 msg = _("Failed to parse %s\n%s") % (f, inst)
143 128 if trusted:
144 129 raise util.Abort(msg)
145 130 self.warn(_("Ignored: %s\n") % msg)
146 131
147 132 if trusted:
148 133 if cdata != self.cdata:
149 134 updateconfig(cdata, self.cdata)
150 135 if self.ucdata is not None:
151 136 updateconfig(cdata, self.ucdata)
152 137 # override data from config files with data set with ui.setconfig
153 138 updateconfig(self.overlay, self.cdata)
154 139 if root is None:
155 140 root = os.path.expanduser('~')
156 141 self.fixconfig(root=root)
157 142
158 143 def readsections(self, filename, *sections):
159 144 """Read filename and add only the specified sections to the config data
160 145
161 146 The settings are added to the trusted config data.
162 147 """
163 148 if not sections:
164 149 return
165 150
166 151 cdata = util.configparser()
167 152 try:
168 153 try:
169 154 fp = open(filename)
170 155 except IOError, inst:
171 156 raise util.Abort(_("unable to open %s: %s") %
172 157 (filename, getattr(inst, "strerror", inst)))
173 158 try:
174 159 cdata.readfp(fp, filename)
175 160 finally:
176 161 fp.close()
177 162 except ConfigParser.ParsingError, inst:
178 163 raise util.Abort(_("failed to parse %s\n%s") % (filename, inst))
179 164
180 165 for section in sections:
181 166 if not cdata.has_section(section):
182 167 cdata.add_section(section)
183 168
184 169 updateconfig(cdata, self.cdata, sections)
185 170 if self.ucdata:
186 171 updateconfig(cdata, self.ucdata, sections)
187 172
188 173 def fixconfig(self, section=None, name=None, value=None, root=None):
189 174 # translate paths relative to root (or home) into absolute paths
190 175 if section is None or section == 'paths':
191 176 if root is None:
192 177 root = os.getcwd()
193 178 items = section and [(name, value)] or []
194 179 for cdata in self.cdata, self.ucdata, self.overlay:
195 180 if not cdata: continue
196 181 if not items and cdata.has_section('paths'):
197 182 pathsitems = cdata.items('paths')
198 183 else:
199 184 pathsitems = items
200 185 for n, path in pathsitems:
201 186 if path and "://" not in path and not os.path.isabs(path):
202 187 cdata.set("paths", n,
203 188 os.path.normpath(os.path.join(root, path)))
204 189
205 190 # update verbosity/interactive/report_untrusted settings
206 191 if section is None or section == 'ui':
207 192 if name is None or name in ('quiet', 'verbose', 'debug'):
208 193 self.verbosity_constraints()
209 194 if name is None or name == 'interactive':
210 195 interactive = self.configbool("ui", "interactive", None)
211 196 if interactive is None and self.interactive:
212 197 self.interactive = self.isatty()
213 198 else:
214 199 self.interactive = interactive
215 200 if name is None or name == 'report_untrusted':
216 201 self.report_untrusted = (
217 202 self.configbool("ui", "report_untrusted", True))
203 self.traceback = self.configbool('ui', 'traceback', False)
218 204
219 205 # update trust information
220 206 if (section is None or section == 'trusted') and self.trusted_users:
221 207 for user in self.configlist('trusted', 'users'):
222 208 self.trusted_users[user] = 1
223 209 for group in self.configlist('trusted', 'groups'):
224 210 self.trusted_groups[group] = 1
225 211
226 212 def setconfig(self, section, name, value):
227 213 for cdata in (self.overlay, self.cdata, self.ucdata):
228 214 if not cdata: continue
229 215 if not cdata.has_section(section):
230 216 cdata.add_section(section)
231 217 cdata.set(section, name, value)
232 218 self.fixconfig(section, name, value)
233 219
234 220 def _get_cdata(self, untrusted):
235 221 if untrusted and self.ucdata:
236 222 return self.ucdata
237 223 return self.cdata
238 224
239 225 def _config(self, section, name, default, funcname, untrusted, abort):
240 226 cdata = self._get_cdata(untrusted)
241 227 if cdata.has_option(section, name):
242 228 try:
243 229 func = getattr(cdata, funcname)
244 230 return func(section, name)
245 231 except (ConfigParser.InterpolationError, ValueError), inst:
246 232 msg = _("Error in configuration section [%s] "
247 233 "parameter '%s':\n%s") % (section, name, inst)
248 234 if abort:
249 235 raise util.Abort(msg)
250 236 self.warn(_("Ignored: %s\n") % msg)
251 237 return default
252 238
253 239 def _configcommon(self, section, name, default, funcname, untrusted):
254 240 value = self._config(section, name, default, funcname,
255 241 untrusted, abort=True)
256 242 if self.debugflag and not untrusted and self.ucdata:
257 243 uvalue = self._config(section, name, None, funcname,
258 244 untrusted=True, abort=False)
259 245 if uvalue is not None and uvalue != value:
260 246 self.warn(_("Ignoring untrusted configuration option "
261 247 "%s.%s = %s\n") % (section, name, uvalue))
262 248 return value
263 249
264 250 def config(self, section, name, default=None, untrusted=False):
265 251 return self._configcommon(section, name, default, 'get', untrusted)
266 252
267 253 def configbool(self, section, name, default=False, untrusted=False):
268 254 return self._configcommon(section, name, default, 'getboolean',
269 255 untrusted)
270 256
271 257 def configlist(self, section, name, default=None, untrusted=False):
272 258 """Return a list of comma/space separated strings"""
273 259 result = self.config(section, name, untrusted=untrusted)
274 260 if result is None:
275 261 result = default or []
276 262 if isinstance(result, basestring):
277 263 result = result.replace(",", " ").split()
278 264 return result
279 265
280 266 def has_section(self, section, untrusted=False):
281 267 '''tell whether section exists in config.'''
282 268 cdata = self._get_cdata(untrusted)
283 269 return cdata.has_section(section)
284 270
285 271 def _configitems(self, section, untrusted, abort):
286 272 items = {}
287 273 cdata = self._get_cdata(untrusted)
288 274 if cdata.has_section(section):
289 275 try:
290 276 items.update(dict(cdata.items(section)))
291 277 except ConfigParser.InterpolationError, inst:
292 278 msg = _("Error in configuration section [%s]:\n"
293 279 "%s") % (section, inst)
294 280 if abort:
295 281 raise util.Abort(msg)
296 282 self.warn(_("Ignored: %s\n") % msg)
297 283 return items
298 284
299 285 def configitems(self, section, untrusted=False):
300 286 items = self._configitems(section, untrusted=untrusted, abort=True)
301 287 if self.debugflag and not untrusted and self.ucdata:
302 288 uitems = self._configitems(section, untrusted=True, abort=False)
303 289 for k in util.sort(uitems):
304 290 if uitems[k] != items.get(k):
305 291 self.warn(_("Ignoring untrusted configuration option "
306 292 "%s.%s = %s\n") % (section, k, uitems[k]))
307 293 return util.sort(items.items())
308 294
309 295 def walkconfig(self, untrusted=False):
310 296 cdata = self._get_cdata(untrusted)
311 297 sections = cdata.sections()
312 298 sections.sort()
313 299 for section in sections:
314 300 for name, value in self.configitems(section, untrusted):
315 301 yield section, name, str(value).replace('\n', '\\n')
316 302
317 303 def username(self):
318 304 """Return default username to be used in commits.
319 305
320 306 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
321 307 and stop searching if one of these is set.
322 308 If not found and ui.askusername is True, ask the user, else use
323 309 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
324 310 """
325 311 user = os.environ.get("HGUSER")
326 312 if user is None:
327 313 user = self.config("ui", "username")
328 314 if user is None:
329 315 user = os.environ.get("EMAIL")
330 316 if user is None and self.configbool("ui", "askusername"):
331 317 user = self.prompt(_("enter a commit username:"), default=None)
332 318 if user is None:
333 319 try:
334 320 user = '%s@%s' % (util.getuser(), socket.getfqdn())
335 321 self.warn(_("No username found, using '%s' instead\n") % user)
336 322 except KeyError:
337 323 pass
338 324 if not user:
339 325 raise util.Abort(_("Please specify a username."))
340 326 if "\n" in user:
341 327 raise util.Abort(_("username %s contains a newline\n") % repr(user))
342 328 return user
343 329
344 330 def shortuser(self, user):
345 331 """Return a short representation of a user name or email address."""
346 332 if not self.verbose: user = util.shortuser(user)
347 333 return user
348 334
349 335 def expandpath(self, loc, default=None):
350 336 """Return repository location relative to cwd or from [paths]"""
351 337 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
352 338 return loc
353 339
354 340 path = self.config("paths", loc)
355 341 if not path and default is not None:
356 342 path = self.config("paths", default)
357 343 return path or loc
358 344
359 345 def pushbuffer(self):
360 346 self.buffers.append([])
361 347
362 348 def popbuffer(self):
363 349 return "".join(self.buffers.pop())
364 350
365 351 def write(self, *args):
366 352 if self.buffers:
367 353 self.buffers[-1].extend([str(a) for a in args])
368 354 else:
369 355 for a in args:
370 356 sys.stdout.write(str(a))
371 357
372 358 def write_err(self, *args):
373 359 try:
374 360 if not sys.stdout.closed: sys.stdout.flush()
375 361 for a in args:
376 362 sys.stderr.write(str(a))
377 363 # stderr may be buffered under win32 when redirected to files,
378 364 # including stdout.
379 365 if not sys.stderr.closed: sys.stderr.flush()
380 366 except IOError, inst:
381 367 if inst.errno != errno.EPIPE:
382 368 raise
383 369
384 370 def flush(self):
385 371 try: sys.stdout.flush()
386 372 except: pass
387 373 try: sys.stderr.flush()
388 374 except: pass
389 375
390 376 def _readline(self, prompt=''):
391 377 if self.isatty():
392 378 try:
393 379 # magically add command line editing support, where
394 380 # available
395 381 import readline
396 382 # force demandimport to really load the module
397 383 readline.read_history_file
398 384 # windows sometimes raises something other than ImportError
399 385 except Exception:
400 386 pass
401 387 line = raw_input(prompt)
402 388 # When stdin is in binary mode on Windows, it can cause
403 389 # raw_input() to emit an extra trailing carriage return
404 390 if os.linesep == '\r\n' and line and line[-1] == '\r':
405 391 line = line[:-1]
406 392 return line
407 393
408 394 def prompt(self, msg, pat=None, default="y"):
409 395 """Prompt user with msg, read response, and ensure it matches pat
410 396
411 397 If not interactive -- the default is returned
412 398 """
413 399 if not self.interactive:
414 400 self.note(msg, ' ', default, "\n")
415 401 return default
416 402 while True:
417 403 try:
418 404 r = self._readline(msg + ' ')
419 405 if not r:
420 406 return default
421 407 if not pat or re.match(pat, r):
422 408 return r
423 409 else:
424 410 self.write(_("unrecognized response\n"))
425 411 except EOFError:
426 412 raise util.Abort(_('response expected'))
427 413
428 414 def getpass(self, prompt=None, default=None):
429 415 if not self.interactive: return default
430 416 try:
431 417 return getpass.getpass(prompt or _('password: '))
432 418 except EOFError:
433 419 raise util.Abort(_('response expected'))
434 420 def status(self, *msg):
435 421 if not self.quiet: self.write(*msg)
436 422 def warn(self, *msg):
437 423 self.write_err(*msg)
438 424 def note(self, *msg):
439 425 if self.verbose: self.write(*msg)
440 426 def debug(self, *msg):
441 427 if self.debugflag: self.write(*msg)
442 428 def edit(self, text, user):
443 429 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
444 430 text=True)
445 431 try:
446 432 f = os.fdopen(fd, "w")
447 433 f.write(text)
448 434 f.close()
449 435
450 436 editor = self.geteditor()
451 437
452 438 util.system("%s \"%s\"" % (editor, name),
453 439 environ={'HGUSER': user},
454 440 onerr=util.Abort, errprefix=_("edit failed"))
455 441
456 442 f = open(name)
457 443 t = f.read()
458 444 f.close()
459 445 t = re.sub("(?m)^HG:.*\n", "", t)
460 446 finally:
461 447 os.unlink(name)
462 448
463 449 return t
464 450
465 451 def print_exc(self):
466 452 '''print exception traceback if traceback printing enabled.
467 453 only to call in exception handler. returns true if traceback
468 454 printed.'''
469 455 if self.traceback:
470 456 traceback.print_exc()
471 457 return self.traceback
472 458
473 459 def geteditor(self):
474 460 '''return editor to use'''
475 461 return (os.environ.get("HGEDITOR") or
476 462 self.config("ui", "editor") or
477 463 os.environ.get("VISUAL") or
478 464 os.environ.get("EDITOR", "vi"))
@@ -1,71 +1,68 b''
1 1 #!/bin/sh
2 2 # Tests if hgweb can run without touching sys.stdin, as is required
3 3 # by the WSGI standard and strictly implemented by mod_wsgi.
4 4
5 5 mkdir repo
6 6 cd repo
7 7 hg init
8 8 echo foo > bar
9 9 hg add bar
10 10 hg commit -m "test" -d "0 0"
11 11 hg tip
12 12
13 13 cat > request.py <<EOF
14 14 from mercurial import dispatch
15 15 from mercurial.hgweb.hgweb_mod import hgweb
16 16 from mercurial.ui import ui
17 17 from mercurial import hg
18 18 from StringIO import StringIO
19 19 import os, sys
20 20
21 21 class FileLike(object):
22 22 def __init__(self, real):
23 23 self.real = real
24 24 def fileno(self):
25 25 print >> sys.__stdout__, 'FILENO'
26 26 return self.real.fileno()
27 27 def read(self):
28 28 print >> sys.__stdout__, 'READ'
29 29 return self.real.read()
30 30 def readline(self):
31 31 print >> sys.__stdout__, 'READLINE'
32 32 return self.real.readline()
33 def isatty(self):
34 print >> sys.__stdout__, 'ISATTY'
35 return False
36 33
37 34 sys.stdin = FileLike(sys.stdin)
38 35 errors = StringIO()
39 36 input = StringIO()
40 37 output = StringIO()
41 38
42 39 def startrsp(headers, data):
43 40 print '---- HEADERS'
44 41 print headers
45 42 print '---- DATA'
46 43 print data
47 44 return output.write
48 45
49 46 env = {
50 47 'wsgi.version': (1, 0),
51 48 'wsgi.url_scheme': 'http',
52 49 'wsgi.errors': errors,
53 50 'wsgi.input': input,
54 51 'wsgi.multithread': False,
55 52 'wsgi.multiprocess': False,
56 53 'wsgi.run_once': False,
57 54 'REQUEST_METHOD': 'GET',
58 55 'SCRIPT_NAME': '',
59 56 'PATH_INFO': '',
60 57 'QUERY_STRING': '',
61 58 'SERVER_NAME': '127.0.0.1',
62 59 'SERVER_PORT': os.environ['HGPORT'],
63 60 'SERVER_PROTOCOL': 'HTTP/1.0'
64 61 }
65 62
66 63 hgweb('.')(env, startrsp)
67 64 print '---- ERRORS'
68 65 print errors.getvalue()
69 66 EOF
70 67
71 68 python request.py
@@ -1,211 +1,211 b''
1 1 # Since it's not easy to write a test that portably deals
2 2 # with files from different users/groups, we cheat a bit by
3 3 # monkey-patching some functions in the util module
4 4
5 5 import os
6 6 from mercurial import ui, util
7 7
8 8 hgrc = os.environ['HGRCPATH']
9 9 f = open(hgrc)
10 10 basehgrc = f.read()
11 11 f.close()
12 12
13 13 def testui(user='foo', group='bar', tusers=(), tgroups=(),
14 14 cuser='foo', cgroup='bar', debug=False, silent=False):
15 15 # user, group => owners of the file
16 16 # tusers, tgroups => trusted users/groups
17 17 # cuser, cgroup => user/group of the current process
18 18
19 19 # write a global hgrc with the list of trusted users/groups and
20 20 # some setting so that we can be sure it was read
21 21 f = open(hgrc, 'w')
22 22 f.write(basehgrc)
23 23 f.write('\n[paths]\n')
24 24 f.write('global = /some/path\n\n')
25 25
26 26 if tusers or tgroups:
27 27 f.write('[trusted]\n')
28 28 if tusers:
29 29 f.write('users = %s\n' % ', '.join(tusers))
30 30 if tgroups:
31 31 f.write('groups = %s\n' % ', '.join(tgroups))
32 32 f.close()
33 33
34 34 # override the functions that give names to uids and gids
35 35 def username(uid=None):
36 36 if uid is None:
37 37 return cuser
38 38 return user
39 39 util.username = username
40 40
41 41 def groupname(gid=None):
42 42 if gid is None:
43 43 return 'bar'
44 44 return group
45 45 util.groupname = groupname
46 46
47 47 def isowner(fp, st=None):
48 48 return user == cuser
49 49 util.isowner = isowner
50 50
51 51 # try to read everything
52 52 #print '# File belongs to user %s, group %s' % (user, group)
53 53 #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
54 54 kind = ('different', 'same')
55 55 who = ('', 'user', 'group', 'user and the group')
56 56 trusted = who[(user in tusers) + 2*(group in tgroups)]
57 57 if trusted:
58 58 trusted = ', but we trust the ' + trusted
59 59 print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
60 60 trusted)
61 61
62 62 parentui = ui.ui()
63 parentui.updateopts(debug=debug)
63 parentui.setconfig('ui', 'debug', str(bool(debug)))
64 64 u = ui.ui(parentui=parentui)
65 65 u.readconfig('.hg/hgrc')
66 66 if silent:
67 67 return u
68 68 print 'trusted'
69 69 for name, path in u.configitems('paths'):
70 70 print ' ', name, '=', path
71 71 print 'untrusted'
72 72 for name, path in u.configitems('paths', untrusted=True):
73 73 print '.',
74 74 u.config('paths', name) # warning with debug=True
75 75 print '.',
76 76 u.config('paths', name, untrusted=True) # no warnings
77 77 print name, '=', path
78 78 print
79 79
80 80 return u
81 81
82 82 os.mkdir('repo')
83 83 os.chdir('repo')
84 84 os.mkdir('.hg')
85 85 f = open('.hg/hgrc', 'w')
86 86 f.write('[paths]\n')
87 87 f.write('local = /another/path\n\n')
88 88 f.write('interpolated = %(global)s%(local)s\n\n')
89 89 f.close()
90 90
91 91 #print '# Everything is run by user foo, group bar\n'
92 92
93 93 # same user, same group
94 94 testui()
95 95 # same user, different group
96 96 testui(group='def')
97 97 # different user, same group
98 98 testui(user='abc')
99 99 # ... but we trust the group
100 100 testui(user='abc', tgroups=['bar'])
101 101 # different user, different group
102 102 testui(user='abc', group='def')
103 103 # ... but we trust the user
104 104 testui(user='abc', group='def', tusers=['abc'])
105 105 # ... but we trust the group
106 106 testui(user='abc', group='def', tgroups=['def'])
107 107 # ... but we trust the user and the group
108 108 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
109 109 # ... but we trust all users
110 110 print '# we trust all users'
111 111 testui(user='abc', group='def', tusers=['*'])
112 112 # ... but we trust all groups
113 113 print '# we trust all groups'
114 114 testui(user='abc', group='def', tgroups=['*'])
115 115 # ... but we trust the whole universe
116 116 print '# we trust all users and groups'
117 117 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
118 118 # ... check that users and groups are in different namespaces
119 119 print "# we don't get confused by users and groups with the same name"
120 120 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
121 121 # ... lists of user names work
122 122 print "# list of user names"
123 123 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
124 124 tgroups=['bar', 'baz', 'qux'])
125 125 # ... lists of group names work
126 126 print "# list of group names"
127 127 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
128 128 tgroups=['bar', 'def', 'baz', 'qux'])
129 129
130 130 print "# Can't figure out the name of the user running this process"
131 131 testui(user='abc', group='def', cuser=None)
132 132
133 133 print "# prints debug warnings"
134 134 u = testui(user='abc', group='def', cuser='foo', debug=True)
135 135
136 136 print "# ui.readsections"
137 137 filename = 'foobar'
138 138 f = open(filename, 'w')
139 139 f.write('[foobar]\n')
140 140 f.write('baz = quux\n')
141 141 f.close()
142 142 u.readsections(filename, 'foobar')
143 143 print u.config('foobar', 'baz')
144 144
145 145 print
146 146 print "# read trusted, untrusted, new ui, trusted"
147 147 u = ui.ui()
148 u.updateopts(debug=True)
148 u.setconfig('ui', 'debug', 'on')
149 149 u.readconfig(filename)
150 150 u2 = ui.ui(parentui=u)
151 151 def username(uid=None):
152 152 return 'foo'
153 153 util.username = username
154 154 u2.readconfig('.hg/hgrc')
155 155 print 'trusted:'
156 156 print u2.config('foobar', 'baz')
157 157 print u2.config('paths', 'interpolated')
158 158 print 'untrusted:'
159 159 print u2.config('foobar', 'baz', untrusted=True)
160 160 print u2.config('paths', 'interpolated', untrusted=True)
161 161
162 162 print
163 163 print "# error handling"
164 164
165 165 def assertraises(f, exc=util.Abort):
166 166 try:
167 167 f()
168 168 except exc, inst:
169 169 print 'raised', inst.__class__.__name__
170 170 else:
171 171 print 'no exception?!'
172 172
173 173 print "# file doesn't exist"
174 174 os.unlink('.hg/hgrc')
175 175 assert not os.path.exists('.hg/hgrc')
176 176 testui(debug=True, silent=True)
177 177 testui(user='abc', group='def', debug=True, silent=True)
178 178
179 179 print
180 180 print "# parse error"
181 181 f = open('.hg/hgrc', 'w')
182 182 f.write('foo = bar')
183 183 f.close()
184 184 testui(user='abc', group='def', silent=True)
185 185 assertraises(lambda: testui(debug=True, silent=True))
186 186
187 187 print
188 188 print "# interpolation error"
189 189 f = open('.hg/hgrc', 'w')
190 190 f.write('[foo]\n')
191 191 f.write('bar = %(')
192 192 f.close()
193 193 u = testui(debug=True, silent=True)
194 194 print '# regular config:'
195 195 print ' trusted',
196 196 assertraises(lambda: u.config('foo', 'bar'))
197 197 print 'untrusted',
198 198 assertraises(lambda: u.config('foo', 'bar', untrusted=True))
199 199
200 200 u = testui(user='abc', group='def', debug=True, silent=True)
201 201 print ' trusted ',
202 202 print u.config('foo', 'bar')
203 203 print 'untrusted',
204 204 assertraises(lambda: u.config('foo', 'bar', untrusted=True))
205 205
206 206 print '# configitems:'
207 207 print ' trusted ',
208 208 print u.configitems('foo')
209 209 print 'untrusted',
210 210 assertraises(lambda: u.configitems('foo', untrusted=True))
211 211
@@ -1,46 +1,49 b''
1 1 #!/usr/bin/env python
2 2
3 3 import os
4 4 from mercurial import ui
5 5
6 6 hgrc = os.environ['HGRCPATH']
7 7 f = open(hgrc)
8 8 basehgrc = f.read()
9 9 f.close()
10 10
11 11 print ' hgrc settings command line options final result '
12 12 print ' quiet verbo debug quiet verbo debug quiet verbo debug'
13 13
14 14 for i in xrange(64):
15 15 hgrc_quiet = bool(i & 1<<0)
16 16 hgrc_verbose = bool(i & 1<<1)
17 17 hgrc_debug = bool(i & 1<<2)
18 18 cmd_quiet = bool(i & 1<<3)
19 19 cmd_verbose = bool(i & 1<<4)
20 20 cmd_debug = bool(i & 1<<5)
21 21
22 22 f = open(hgrc, 'w')
23 23 f.write(basehgrc)
24 24 f.write('\n[ui]\n')
25 25 if hgrc_quiet:
26 26 f.write('quiet = True\n')
27 27 if hgrc_verbose:
28 28 f.write('verbose = True\n')
29 29 if hgrc_debug:
30 30 f.write('debug = True\n')
31 31 f.close()
32 32
33 33 u = ui.ui()
34 u.updateopts(quiet=cmd_quiet, verbose=cmd_verbose, debug=cmd_debug)
34 if cmd_quiet or cmd_debug or cmd_verbose:
35 u.setconfig('ui', 'quiet', str(bool(cmd_quiet)))
36 u.setconfig('ui', 'verbose', str(bool(cmd_verbose)))
37 u.setconfig('ui', 'debug', str(bool(cmd_debug)))
35 38
36 39 check = ''
37 40 if u.debugflag:
38 41 if not u.verbose or u.quiet:
39 42 check = ' *'
40 43 elif u.verbose and u.quiet:
41 44 check = ' +'
42 45
43 46 print ('%2d %5s %5s %5s %5s %5s %5s -> %5s %5s %5s%s'
44 47 % (i, hgrc_quiet, hgrc_verbose, hgrc_debug,
45 48 cmd_quiet, cmd_verbose, cmd_debug,
46 49 u.quiet, u.verbose, u.debugflag, check))
General Comments 0
You need to be logged in to leave comments. Login now