##// END OF EJS Templates
ui: use I/O descriptors internally...
Idan Kamara -
r14614:afccc64e default
parent child Browse files
Show More
@@ -1,88 +1,92
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
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 import cgi, cStringIO, zlib, sys, urllib
8 import cgi, cStringIO, zlib, urllib
9 from mercurial import util, wireproto
9 from mercurial import util, wireproto
10 from common import HTTP_OK
10 from common import HTTP_OK
11
11
12 HGTYPE = 'application/mercurial-0.1'
12 HGTYPE = 'application/mercurial-0.1'
13
13
14 class webproto(object):
14 class webproto(object):
15 def __init__(self, req):
15 def __init__(self, req, ui):
16 self.req = req
16 self.req = req
17 self.response = ''
17 self.response = ''
18 self.ui = ui
18 def getargs(self, args):
19 def getargs(self, args):
19 knownargs = self._args()
20 knownargs = self._args()
20 data = {}
21 data = {}
21 keys = args.split()
22 keys = args.split()
22 for k in keys:
23 for k in keys:
23 if k == '*':
24 if k == '*':
24 star = {}
25 star = {}
25 for key in knownargs.keys():
26 for key in knownargs.keys():
26 if key != 'cmd' and key not in keys:
27 if key != 'cmd' and key not in keys:
27 star[key] = knownargs[key][0]
28 star[key] = knownargs[key][0]
28 data['*'] = star
29 data['*'] = star
29 else:
30 else:
30 data[k] = knownargs[k][0]
31 data[k] = knownargs[k][0]
31 return [data[k] for k in keys]
32 return [data[k] for k in keys]
32 def _args(self):
33 def _args(self):
33 args = self.req.form.copy()
34 args = self.req.form.copy()
34 chunks = []
35 chunks = []
35 i = 1
36 i = 1
36 while True:
37 while True:
37 h = self.req.env.get('HTTP_X_HGARG_' + str(i))
38 h = self.req.env.get('HTTP_X_HGARG_' + str(i))
38 if h is None:
39 if h is None:
39 break
40 break
40 chunks += [h]
41 chunks += [h]
41 i += 1
42 i += 1
42 args.update(cgi.parse_qs(''.join(chunks), keep_blank_values=True))
43 args.update(cgi.parse_qs(''.join(chunks), keep_blank_values=True))
43 return args
44 return args
44 def getfile(self, fp):
45 def getfile(self, fp):
45 length = int(self.req.env['CONTENT_LENGTH'])
46 length = int(self.req.env['CONTENT_LENGTH'])
46 for s in util.filechunkiter(self.req, limit=length):
47 for s in util.filechunkiter(self.req, limit=length):
47 fp.write(s)
48 fp.write(s)
48 def redirect(self):
49 def redirect(self):
49 self.oldio = sys.stdout, sys.stderr
50 self.oldio = self.ui.fout, self.ui.ferr
50 sys.stderr = sys.stdout = cStringIO.StringIO()
51 self.ui.ferr = self.ui.fout = cStringIO.StringIO()
52 def restore(self):
53 val = self.ui.fout.getvalue()
54 self.ui.ferr, self.ui.fout = self.oldio
55 return val
51 def groupchunks(self, cg):
56 def groupchunks(self, cg):
52 z = zlib.compressobj()
57 z = zlib.compressobj()
53 while True:
58 while True:
54 chunk = cg.read(4096)
59 chunk = cg.read(4096)
55 if not chunk:
60 if not chunk:
56 break
61 break
57 yield z.compress(chunk)
62 yield z.compress(chunk)
58 yield z.flush()
63 yield z.flush()
59 def _client(self):
64 def _client(self):
60 return 'remote:%s:%s:%s' % (
65 return 'remote:%s:%s:%s' % (
61 self.req.env.get('wsgi.url_scheme') or 'http',
66 self.req.env.get('wsgi.url_scheme') or 'http',
62 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
67 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
63 urllib.quote(self.req.env.get('REMOTE_USER', '')))
68 urllib.quote(self.req.env.get('REMOTE_USER', '')))
64
69
65 def iscmd(cmd):
70 def iscmd(cmd):
66 return cmd in wireproto.commands
71 return cmd in wireproto.commands
67
72
68 def call(repo, req, cmd):
73 def call(repo, req, cmd):
69 p = webproto(req)
74 p = webproto(req, repo.ui)
70 rsp = wireproto.dispatch(repo, p, cmd)
75 rsp = wireproto.dispatch(repo, p, cmd)
71 if isinstance(rsp, str):
76 if isinstance(rsp, str):
72 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
77 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
73 return [rsp]
78 return [rsp]
74 elif isinstance(rsp, wireproto.streamres):
79 elif isinstance(rsp, wireproto.streamres):
75 req.respond(HTTP_OK, HGTYPE)
80 req.respond(HTTP_OK, HGTYPE)
76 return rsp.gen
81 return rsp.gen
77 elif isinstance(rsp, wireproto.pushres):
82 elif isinstance(rsp, wireproto.pushres):
78 val = sys.stdout.getvalue()
83 val = p.restore()
79 sys.stdout, sys.stderr = p.oldio
80 req.respond(HTTP_OK, HGTYPE)
84 req.respond(HTTP_OK, HGTYPE)
81 return ['%d\n%s' % (rsp.res, val)]
85 return ['%d\n%s' % (rsp.res, val)]
82 elif isinstance(rsp, wireproto.pusherr):
86 elif isinstance(rsp, wireproto.pusherr):
83 # drain the incoming bundle
87 # drain the incoming bundle
84 req.drain()
88 req.drain()
85 sys.stdout, sys.stderr = p.oldio
89 p.restore()
86 rsp = '0\n%s\n' % rsp.res
90 rsp = '0\n%s\n' % rsp.res
87 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
91 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
88 return [rsp]
92 return [rsp]
@@ -1,144 +1,144
1 # sshserver.py - ssh protocol server support for mercurial
1 # sshserver.py - ssh protocol server support for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import util, hook, wireproto, changegroup
9 import util, hook, wireproto, changegroup
10 import os, sys
10 import os, sys
11
11
12 class sshserver(object):
12 class sshserver(object):
13 def __init__(self, ui, repo):
13 def __init__(self, ui, repo):
14 self.ui = ui
14 self.ui = ui
15 self.repo = repo
15 self.repo = repo
16 self.lock = None
16 self.lock = None
17 self.fin = sys.stdin
17 self.fin = ui.fin
18 self.fout = sys.stdout
18 self.fout = ui.fout
19
19
20 hook.redirect(True)
20 hook.redirect(True)
21 sys.stdout = sys.stderr
21 ui.fout = repo.ui.fout = ui.ferr
22
22
23 # Prevent insertion/deletion of CRs
23 # Prevent insertion/deletion of CRs
24 util.setbinary(self.fin)
24 util.setbinary(self.fin)
25 util.setbinary(self.fout)
25 util.setbinary(self.fout)
26
26
27 def getargs(self, args):
27 def getargs(self, args):
28 data = {}
28 data = {}
29 keys = args.split()
29 keys = args.split()
30 for n in xrange(len(keys)):
30 for n in xrange(len(keys)):
31 argline = self.fin.readline()[:-1]
31 argline = self.fin.readline()[:-1]
32 arg, l = argline.split()
32 arg, l = argline.split()
33 if arg not in keys:
33 if arg not in keys:
34 raise util.Abort("unexpected parameter %r" % arg)
34 raise util.Abort("unexpected parameter %r" % arg)
35 if arg == '*':
35 if arg == '*':
36 star = {}
36 star = {}
37 for k in xrange(int(l)):
37 for k in xrange(int(l)):
38 argline = self.fin.readline()[:-1]
38 argline = self.fin.readline()[:-1]
39 arg, l = argline.split()
39 arg, l = argline.split()
40 val = self.fin.read(int(l))
40 val = self.fin.read(int(l))
41 star[arg] = val
41 star[arg] = val
42 data['*'] = star
42 data['*'] = star
43 else:
43 else:
44 val = self.fin.read(int(l))
44 val = self.fin.read(int(l))
45 data[arg] = val
45 data[arg] = val
46 return [data[k] for k in keys]
46 return [data[k] for k in keys]
47
47
48 def getarg(self, name):
48 def getarg(self, name):
49 return self.getargs(name)[0]
49 return self.getargs(name)[0]
50
50
51 def getfile(self, fpout):
51 def getfile(self, fpout):
52 self.sendresponse('')
52 self.sendresponse('')
53 count = int(self.fin.readline())
53 count = int(self.fin.readline())
54 while count:
54 while count:
55 fpout.write(self.fin.read(count))
55 fpout.write(self.fin.read(count))
56 count = int(self.fin.readline())
56 count = int(self.fin.readline())
57
57
58 def redirect(self):
58 def redirect(self):
59 pass
59 pass
60
60
61 def groupchunks(self, changegroup):
61 def groupchunks(self, changegroup):
62 while True:
62 while True:
63 d = changegroup.read(4096)
63 d = changegroup.read(4096)
64 if not d:
64 if not d:
65 break
65 break
66 yield d
66 yield d
67
67
68 def sendresponse(self, v):
68 def sendresponse(self, v):
69 self.fout.write("%d\n" % len(v))
69 self.fout.write("%d\n" % len(v))
70 self.fout.write(v)
70 self.fout.write(v)
71 self.fout.flush()
71 self.fout.flush()
72
72
73 def sendstream(self, source):
73 def sendstream(self, source):
74 for chunk in source.gen:
74 for chunk in source.gen:
75 self.fout.write(chunk)
75 self.fout.write(chunk)
76 self.fout.flush()
76 self.fout.flush()
77
77
78 def sendpushresponse(self, rsp):
78 def sendpushresponse(self, rsp):
79 self.sendresponse('')
79 self.sendresponse('')
80 self.sendresponse(str(rsp.res))
80 self.sendresponse(str(rsp.res))
81
81
82 def sendpusherror(self, rsp):
82 def sendpusherror(self, rsp):
83 self.sendresponse(rsp.res)
83 self.sendresponse(rsp.res)
84
84
85 def serve_forever(self):
85 def serve_forever(self):
86 try:
86 try:
87 while self.serve_one():
87 while self.serve_one():
88 pass
88 pass
89 finally:
89 finally:
90 if self.lock is not None:
90 if self.lock is not None:
91 self.lock.release()
91 self.lock.release()
92 sys.exit(0)
92 sys.exit(0)
93
93
94 handlers = {
94 handlers = {
95 str: sendresponse,
95 str: sendresponse,
96 wireproto.streamres: sendstream,
96 wireproto.streamres: sendstream,
97 wireproto.pushres: sendpushresponse,
97 wireproto.pushres: sendpushresponse,
98 wireproto.pusherr: sendpusherror,
98 wireproto.pusherr: sendpusherror,
99 }
99 }
100
100
101 def serve_one(self):
101 def serve_one(self):
102 cmd = self.fin.readline()[:-1]
102 cmd = self.fin.readline()[:-1]
103 if cmd and cmd in wireproto.commands:
103 if cmd and cmd in wireproto.commands:
104 rsp = wireproto.dispatch(self.repo, self, cmd)
104 rsp = wireproto.dispatch(self.repo, self, cmd)
105 self.handlers[rsp.__class__](self, rsp)
105 self.handlers[rsp.__class__](self, rsp)
106 elif cmd:
106 elif cmd:
107 impl = getattr(self, 'do_' + cmd, None)
107 impl = getattr(self, 'do_' + cmd, None)
108 if impl:
108 if impl:
109 r = impl()
109 r = impl()
110 if r is not None:
110 if r is not None:
111 self.sendresponse(r)
111 self.sendresponse(r)
112 else: self.sendresponse("")
112 else: self.sendresponse("")
113 return cmd != ''
113 return cmd != ''
114
114
115 def do_lock(self):
115 def do_lock(self):
116 '''DEPRECATED - allowing remote client to lock repo is not safe'''
116 '''DEPRECATED - allowing remote client to lock repo is not safe'''
117
117
118 self.lock = self.repo.lock()
118 self.lock = self.repo.lock()
119 return ""
119 return ""
120
120
121 def do_unlock(self):
121 def do_unlock(self):
122 '''DEPRECATED'''
122 '''DEPRECATED'''
123
123
124 if self.lock:
124 if self.lock:
125 self.lock.release()
125 self.lock.release()
126 self.lock = None
126 self.lock = None
127 return ""
127 return ""
128
128
129 def do_addchangegroup(self):
129 def do_addchangegroup(self):
130 '''DEPRECATED'''
130 '''DEPRECATED'''
131
131
132 if not self.lock:
132 if not self.lock:
133 self.sendresponse("not locked")
133 self.sendresponse("not locked")
134 return
134 return
135
135
136 self.sendresponse("")
136 self.sendresponse("")
137 cg = changegroup.unbundle10(self.fin, "UN")
137 cg = changegroup.unbundle10(self.fin, "UN")
138 r = self.repo.addchangegroup(cg, 'serve', self._client(),
138 r = self.repo.addchangegroup(cg, 'serve', self._client(),
139 lock=self.lock)
139 lock=self.lock)
140 return str(r)
140 return str(r)
141
141
142 def _client(self):
142 def _client(self):
143 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
143 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
144 return 'remote:ssh:' + client
144 return 'remote:ssh:' + client
@@ -1,704 +1,711
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits 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 errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error
10 import config, scmutil, util, error
11
11
12 class ui(object):
12 class ui(object):
13 def __init__(self, src=None):
13 def __init__(self, src=None):
14 self._buffers = []
14 self._buffers = []
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self._reportuntrusted = True
16 self._reportuntrusted = True
17 self._ocfg = config.config() # overlay
17 self._ocfg = config.config() # overlay
18 self._tcfg = config.config() # trusted
18 self._tcfg = config.config() # trusted
19 self._ucfg = config.config() # untrusted
19 self._ucfg = config.config() # untrusted
20 self._trustusers = set()
20 self._trustusers = set()
21 self._trustgroups = set()
21 self._trustgroups = set()
22
22
23 if src:
23 if src:
24 self.fout = src.fout
24 self.fout = src.fout
25 self.ferr = src.ferr
25 self.ferr = src.ferr
26 self.fin = src.fin
26 self.fin = src.fin
27
27
28 self._tcfg = src._tcfg.copy()
28 self._tcfg = src._tcfg.copy()
29 self._ucfg = src._ucfg.copy()
29 self._ucfg = src._ucfg.copy()
30 self._ocfg = src._ocfg.copy()
30 self._ocfg = src._ocfg.copy()
31 self._trustusers = src._trustusers.copy()
31 self._trustusers = src._trustusers.copy()
32 self._trustgroups = src._trustgroups.copy()
32 self._trustgroups = src._trustgroups.copy()
33 self.environ = src.environ
33 self.environ = src.environ
34 self.fixconfig()
34 self.fixconfig()
35 else:
35 else:
36 self.fout = sys.stdout
36 self.fout = sys.stdout
37 self.ferr = sys.stderr
37 self.ferr = sys.stderr
38 self.fin = sys.stdin
38 self.fin = sys.stdin
39
39
40 # shared read-only environment
40 # shared read-only environment
41 self.environ = os.environ
41 self.environ = os.environ
42 # we always trust global config files
42 # we always trust global config files
43 for f in scmutil.rcpath():
43 for f in scmutil.rcpath():
44 self.readconfig(f, trust=True)
44 self.readconfig(f, trust=True)
45
45
46 def copy(self):
46 def copy(self):
47 return self.__class__(self)
47 return self.__class__(self)
48
48
49 def _is_trusted(self, fp, f):
49 def _is_trusted(self, fp, f):
50 st = util.fstat(fp)
50 st = util.fstat(fp)
51 if util.isowner(st):
51 if util.isowner(st):
52 return True
52 return True
53
53
54 tusers, tgroups = self._trustusers, self._trustgroups
54 tusers, tgroups = self._trustusers, self._trustgroups
55 if '*' in tusers or '*' in tgroups:
55 if '*' in tusers or '*' in tgroups:
56 return True
56 return True
57
57
58 user = util.username(st.st_uid)
58 user = util.username(st.st_uid)
59 group = util.groupname(st.st_gid)
59 group = util.groupname(st.st_gid)
60 if user in tusers or group in tgroups or user == util.username():
60 if user in tusers or group in tgroups or user == util.username():
61 return True
61 return True
62
62
63 if self._reportuntrusted:
63 if self._reportuntrusted:
64 self.warn(_('Not trusting file %s from untrusted '
64 self.warn(_('Not trusting file %s from untrusted '
65 'user %s, group %s\n') % (f, user, group))
65 'user %s, group %s\n') % (f, user, group))
66 return False
66 return False
67
67
68 def readconfig(self, filename, root=None, trust=False,
68 def readconfig(self, filename, root=None, trust=False,
69 sections=None, remap=None):
69 sections=None, remap=None):
70 try:
70 try:
71 fp = open(filename)
71 fp = open(filename)
72 except IOError:
72 except IOError:
73 if not sections: # ignore unless we were looking for something
73 if not sections: # ignore unless we were looking for something
74 return
74 return
75 raise
75 raise
76
76
77 cfg = config.config()
77 cfg = config.config()
78 trusted = sections or trust or self._is_trusted(fp, filename)
78 trusted = sections or trust or self._is_trusted(fp, filename)
79
79
80 try:
80 try:
81 cfg.read(filename, fp, sections=sections, remap=remap)
81 cfg.read(filename, fp, sections=sections, remap=remap)
82 except error.ConfigError, inst:
82 except error.ConfigError, inst:
83 if trusted:
83 if trusted:
84 raise
84 raise
85 self.warn(_("Ignored: %s\n") % str(inst))
85 self.warn(_("Ignored: %s\n") % str(inst))
86
86
87 if self.plain():
87 if self.plain():
88 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
88 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
89 'logtemplate', 'style',
89 'logtemplate', 'style',
90 'traceback', 'verbose'):
90 'traceback', 'verbose'):
91 if k in cfg['ui']:
91 if k in cfg['ui']:
92 del cfg['ui'][k]
92 del cfg['ui'][k]
93 for k, v in cfg.items('defaults'):
93 for k, v in cfg.items('defaults'):
94 del cfg['defaults'][k]
94 del cfg['defaults'][k]
95 # Don't remove aliases from the configuration if in the exceptionlist
95 # Don't remove aliases from the configuration if in the exceptionlist
96 if self.plain('alias'):
96 if self.plain('alias'):
97 for k, v in cfg.items('alias'):
97 for k, v in cfg.items('alias'):
98 del cfg['alias'][k]
98 del cfg['alias'][k]
99
99
100 if trusted:
100 if trusted:
101 self._tcfg.update(cfg)
101 self._tcfg.update(cfg)
102 self._tcfg.update(self._ocfg)
102 self._tcfg.update(self._ocfg)
103 self._ucfg.update(cfg)
103 self._ucfg.update(cfg)
104 self._ucfg.update(self._ocfg)
104 self._ucfg.update(self._ocfg)
105
105
106 if root is None:
106 if root is None:
107 root = os.path.expanduser('~')
107 root = os.path.expanduser('~')
108 self.fixconfig(root=root)
108 self.fixconfig(root=root)
109
109
110 def fixconfig(self, root=None, section=None):
110 def fixconfig(self, root=None, section=None):
111 if section in (None, 'paths'):
111 if section in (None, 'paths'):
112 # expand vars and ~
112 # expand vars and ~
113 # translate paths relative to root (or home) into absolute paths
113 # translate paths relative to root (or home) into absolute paths
114 root = root or os.getcwd()
114 root = root or os.getcwd()
115 for c in self._tcfg, self._ucfg, self._ocfg:
115 for c in self._tcfg, self._ucfg, self._ocfg:
116 for n, p in c.items('paths'):
116 for n, p in c.items('paths'):
117 if not p:
117 if not p:
118 continue
118 continue
119 if '%%' in p:
119 if '%%' in p:
120 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
120 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
121 % (n, p, self.configsource('paths', n)))
121 % (n, p, self.configsource('paths', n)))
122 p = p.replace('%%', '%')
122 p = p.replace('%%', '%')
123 p = util.expandpath(p)
123 p = util.expandpath(p)
124 if not util.hasscheme(p) and not os.path.isabs(p):
124 if not util.hasscheme(p) and not os.path.isabs(p):
125 p = os.path.normpath(os.path.join(root, p))
125 p = os.path.normpath(os.path.join(root, p))
126 c.set("paths", n, p)
126 c.set("paths", n, p)
127
127
128 if section in (None, 'ui'):
128 if section in (None, 'ui'):
129 # update ui options
129 # update ui options
130 self.debugflag = self.configbool('ui', 'debug')
130 self.debugflag = self.configbool('ui', 'debug')
131 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
131 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
132 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
132 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
133 if self.verbose and self.quiet:
133 if self.verbose and self.quiet:
134 self.quiet = self.verbose = False
134 self.quiet = self.verbose = False
135 self._reportuntrusted = self.debugflag or self.configbool("ui",
135 self._reportuntrusted = self.debugflag or self.configbool("ui",
136 "report_untrusted", True)
136 "report_untrusted", True)
137 self.tracebackflag = self.configbool('ui', 'traceback', False)
137 self.tracebackflag = self.configbool('ui', 'traceback', False)
138
138
139 if section in (None, 'trusted'):
139 if section in (None, 'trusted'):
140 # update trust information
140 # update trust information
141 self._trustusers.update(self.configlist('trusted', 'users'))
141 self._trustusers.update(self.configlist('trusted', 'users'))
142 self._trustgroups.update(self.configlist('trusted', 'groups'))
142 self._trustgroups.update(self.configlist('trusted', 'groups'))
143
143
144 def setconfig(self, section, name, value, overlay=True):
144 def setconfig(self, section, name, value, overlay=True):
145 if overlay:
145 if overlay:
146 self._ocfg.set(section, name, value)
146 self._ocfg.set(section, name, value)
147 self._tcfg.set(section, name, value)
147 self._tcfg.set(section, name, value)
148 self._ucfg.set(section, name, value)
148 self._ucfg.set(section, name, value)
149 self.fixconfig(section=section)
149 self.fixconfig(section=section)
150
150
151 def _data(self, untrusted):
151 def _data(self, untrusted):
152 return untrusted and self._ucfg or self._tcfg
152 return untrusted and self._ucfg or self._tcfg
153
153
154 def configsource(self, section, name, untrusted=False):
154 def configsource(self, section, name, untrusted=False):
155 return self._data(untrusted).source(section, name) or 'none'
155 return self._data(untrusted).source(section, name) or 'none'
156
156
157 def config(self, section, name, default=None, untrusted=False):
157 def config(self, section, name, default=None, untrusted=False):
158 value = self._data(untrusted).get(section, name, default)
158 value = self._data(untrusted).get(section, name, default)
159 if self.debugflag and not untrusted and self._reportuntrusted:
159 if self.debugflag and not untrusted and self._reportuntrusted:
160 uvalue = self._ucfg.get(section, name)
160 uvalue = self._ucfg.get(section, name)
161 if uvalue is not None and uvalue != value:
161 if uvalue is not None and uvalue != value:
162 self.debug(_("ignoring untrusted configuration option "
162 self.debug(_("ignoring untrusted configuration option "
163 "%s.%s = %s\n") % (section, name, uvalue))
163 "%s.%s = %s\n") % (section, name, uvalue))
164 return value
164 return value
165
165
166 def configpath(self, section, name, default=None, untrusted=False):
166 def configpath(self, section, name, default=None, untrusted=False):
167 'get a path config item, expanded relative to config file'
167 'get a path config item, expanded relative to config file'
168 v = self.config(section, name, default, untrusted)
168 v = self.config(section, name, default, untrusted)
169 if not os.path.isabs(v) or "://" not in v:
169 if not os.path.isabs(v) or "://" not in v:
170 src = self.configsource(section, name, untrusted)
170 src = self.configsource(section, name, untrusted)
171 if ':' in src:
171 if ':' in src:
172 base = os.path.dirname(src.rsplit(':'))
172 base = os.path.dirname(src.rsplit(':'))
173 v = os.path.join(base, os.path.expanduser(v))
173 v = os.path.join(base, os.path.expanduser(v))
174 return v
174 return v
175
175
176 def configbool(self, section, name, default=False, untrusted=False):
176 def configbool(self, section, name, default=False, untrusted=False):
177 """parse a configuration element as a boolean
177 """parse a configuration element as a boolean
178
178
179 >>> u = ui(); s = 'foo'
179 >>> u = ui(); s = 'foo'
180 >>> u.setconfig(s, 'true', 'yes')
180 >>> u.setconfig(s, 'true', 'yes')
181 >>> u.configbool(s, 'true')
181 >>> u.configbool(s, 'true')
182 True
182 True
183 >>> u.setconfig(s, 'false', 'no')
183 >>> u.setconfig(s, 'false', 'no')
184 >>> u.configbool(s, 'false')
184 >>> u.configbool(s, 'false')
185 False
185 False
186 >>> u.configbool(s, 'unknown')
186 >>> u.configbool(s, 'unknown')
187 False
187 False
188 >>> u.configbool(s, 'unknown', True)
188 >>> u.configbool(s, 'unknown', True)
189 True
189 True
190 >>> u.setconfig(s, 'invalid', 'somevalue')
190 >>> u.setconfig(s, 'invalid', 'somevalue')
191 >>> u.configbool(s, 'invalid')
191 >>> u.configbool(s, 'invalid')
192 Traceback (most recent call last):
192 Traceback (most recent call last):
193 ...
193 ...
194 ConfigError: foo.invalid is not a boolean ('somevalue')
194 ConfigError: foo.invalid is not a boolean ('somevalue')
195 """
195 """
196
196
197 v = self.config(section, name, None, untrusted)
197 v = self.config(section, name, None, untrusted)
198 if v is None:
198 if v is None:
199 return default
199 return default
200 if isinstance(v, bool):
200 if isinstance(v, bool):
201 return v
201 return v
202 b = util.parsebool(v)
202 b = util.parsebool(v)
203 if b is None:
203 if b is None:
204 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
204 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
205 % (section, name, v))
205 % (section, name, v))
206 return b
206 return b
207
207
208 def configint(self, section, name, default=None, untrusted=False):
208 def configint(self, section, name, default=None, untrusted=False):
209 """parse a configuration element as an integer
209 """parse a configuration element as an integer
210
210
211 >>> u = ui(); s = 'foo'
211 >>> u = ui(); s = 'foo'
212 >>> u.setconfig(s, 'int1', '42')
212 >>> u.setconfig(s, 'int1', '42')
213 >>> u.configint(s, 'int1')
213 >>> u.configint(s, 'int1')
214 42
214 42
215 >>> u.setconfig(s, 'int2', '-42')
215 >>> u.setconfig(s, 'int2', '-42')
216 >>> u.configint(s, 'int2')
216 >>> u.configint(s, 'int2')
217 -42
217 -42
218 >>> u.configint(s, 'unknown', 7)
218 >>> u.configint(s, 'unknown', 7)
219 7
219 7
220 >>> u.setconfig(s, 'invalid', 'somevalue')
220 >>> u.setconfig(s, 'invalid', 'somevalue')
221 >>> u.configint(s, 'invalid')
221 >>> u.configint(s, 'invalid')
222 Traceback (most recent call last):
222 Traceback (most recent call last):
223 ...
223 ...
224 ConfigError: foo.invalid is not an integer ('somevalue')
224 ConfigError: foo.invalid is not an integer ('somevalue')
225 """
225 """
226
226
227 v = self.config(section, name, None, untrusted)
227 v = self.config(section, name, None, untrusted)
228 if v is None:
228 if v is None:
229 return default
229 return default
230 try:
230 try:
231 return int(v)
231 return int(v)
232 except ValueError:
232 except ValueError:
233 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
233 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
234 % (section, name, v))
234 % (section, name, v))
235
235
236 def configlist(self, section, name, default=None, untrusted=False):
236 def configlist(self, section, name, default=None, untrusted=False):
237 """parse a configuration element as a list of comma/space separated
237 """parse a configuration element as a list of comma/space separated
238 strings
238 strings
239
239
240 >>> u = ui(); s = 'foo'
240 >>> u = ui(); s = 'foo'
241 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
241 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
242 >>> u.configlist(s, 'list1')
242 >>> u.configlist(s, 'list1')
243 ['this', 'is', 'a small', 'test']
243 ['this', 'is', 'a small', 'test']
244 """
244 """
245
245
246 def _parse_plain(parts, s, offset):
246 def _parse_plain(parts, s, offset):
247 whitespace = False
247 whitespace = False
248 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
248 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
249 whitespace = True
249 whitespace = True
250 offset += 1
250 offset += 1
251 if offset >= len(s):
251 if offset >= len(s):
252 return None, parts, offset
252 return None, parts, offset
253 if whitespace:
253 if whitespace:
254 parts.append('')
254 parts.append('')
255 if s[offset] == '"' and not parts[-1]:
255 if s[offset] == '"' and not parts[-1]:
256 return _parse_quote, parts, offset + 1
256 return _parse_quote, parts, offset + 1
257 elif s[offset] == '"' and parts[-1][-1] == '\\':
257 elif s[offset] == '"' and parts[-1][-1] == '\\':
258 parts[-1] = parts[-1][:-1] + s[offset]
258 parts[-1] = parts[-1][:-1] + s[offset]
259 return _parse_plain, parts, offset + 1
259 return _parse_plain, parts, offset + 1
260 parts[-1] += s[offset]
260 parts[-1] += s[offset]
261 return _parse_plain, parts, offset + 1
261 return _parse_plain, parts, offset + 1
262
262
263 def _parse_quote(parts, s, offset):
263 def _parse_quote(parts, s, offset):
264 if offset < len(s) and s[offset] == '"': # ""
264 if offset < len(s) and s[offset] == '"': # ""
265 parts.append('')
265 parts.append('')
266 offset += 1
266 offset += 1
267 while offset < len(s) and (s[offset].isspace() or
267 while offset < len(s) and (s[offset].isspace() or
268 s[offset] == ','):
268 s[offset] == ','):
269 offset += 1
269 offset += 1
270 return _parse_plain, parts, offset
270 return _parse_plain, parts, offset
271
271
272 while offset < len(s) and s[offset] != '"':
272 while offset < len(s) and s[offset] != '"':
273 if (s[offset] == '\\' and offset + 1 < len(s)
273 if (s[offset] == '\\' and offset + 1 < len(s)
274 and s[offset + 1] == '"'):
274 and s[offset + 1] == '"'):
275 offset += 1
275 offset += 1
276 parts[-1] += '"'
276 parts[-1] += '"'
277 else:
277 else:
278 parts[-1] += s[offset]
278 parts[-1] += s[offset]
279 offset += 1
279 offset += 1
280
280
281 if offset >= len(s):
281 if offset >= len(s):
282 real_parts = _configlist(parts[-1])
282 real_parts = _configlist(parts[-1])
283 if not real_parts:
283 if not real_parts:
284 parts[-1] = '"'
284 parts[-1] = '"'
285 else:
285 else:
286 real_parts[0] = '"' + real_parts[0]
286 real_parts[0] = '"' + real_parts[0]
287 parts = parts[:-1]
287 parts = parts[:-1]
288 parts.extend(real_parts)
288 parts.extend(real_parts)
289 return None, parts, offset
289 return None, parts, offset
290
290
291 offset += 1
291 offset += 1
292 while offset < len(s) and s[offset] in [' ', ',']:
292 while offset < len(s) and s[offset] in [' ', ',']:
293 offset += 1
293 offset += 1
294
294
295 if offset < len(s):
295 if offset < len(s):
296 if offset + 1 == len(s) and s[offset] == '"':
296 if offset + 1 == len(s) and s[offset] == '"':
297 parts[-1] += '"'
297 parts[-1] += '"'
298 offset += 1
298 offset += 1
299 else:
299 else:
300 parts.append('')
300 parts.append('')
301 else:
301 else:
302 return None, parts, offset
302 return None, parts, offset
303
303
304 return _parse_plain, parts, offset
304 return _parse_plain, parts, offset
305
305
306 def _configlist(s):
306 def _configlist(s):
307 s = s.rstrip(' ,')
307 s = s.rstrip(' ,')
308 if not s:
308 if not s:
309 return []
309 return []
310 parser, parts, offset = _parse_plain, [''], 0
310 parser, parts, offset = _parse_plain, [''], 0
311 while parser:
311 while parser:
312 parser, parts, offset = parser(parts, s, offset)
312 parser, parts, offset = parser(parts, s, offset)
313 return parts
313 return parts
314
314
315 result = self.config(section, name, untrusted=untrusted)
315 result = self.config(section, name, untrusted=untrusted)
316 if result is None:
316 if result is None:
317 result = default or []
317 result = default or []
318 if isinstance(result, basestring):
318 if isinstance(result, basestring):
319 result = _configlist(result.lstrip(' ,\n'))
319 result = _configlist(result.lstrip(' ,\n'))
320 if result is None:
320 if result is None:
321 result = default or []
321 result = default or []
322 return result
322 return result
323
323
324 def has_section(self, section, untrusted=False):
324 def has_section(self, section, untrusted=False):
325 '''tell whether section exists in config.'''
325 '''tell whether section exists in config.'''
326 return section in self._data(untrusted)
326 return section in self._data(untrusted)
327
327
328 def configitems(self, section, untrusted=False):
328 def configitems(self, section, untrusted=False):
329 items = self._data(untrusted).items(section)
329 items = self._data(untrusted).items(section)
330 if self.debugflag and not untrusted and self._reportuntrusted:
330 if self.debugflag and not untrusted and self._reportuntrusted:
331 for k, v in self._ucfg.items(section):
331 for k, v in self._ucfg.items(section):
332 if self._tcfg.get(section, k) != v:
332 if self._tcfg.get(section, k) != v:
333 self.debug(_("ignoring untrusted configuration option "
333 self.debug(_("ignoring untrusted configuration option "
334 "%s.%s = %s\n") % (section, k, v))
334 "%s.%s = %s\n") % (section, k, v))
335 return items
335 return items
336
336
337 def walkconfig(self, untrusted=False):
337 def walkconfig(self, untrusted=False):
338 cfg = self._data(untrusted)
338 cfg = self._data(untrusted)
339 for section in cfg.sections():
339 for section in cfg.sections():
340 for name, value in self.configitems(section, untrusted):
340 for name, value in self.configitems(section, untrusted):
341 yield section, name, value
341 yield section, name, value
342
342
343 def plain(self, feature=None):
343 def plain(self, feature=None):
344 '''is plain mode active?
344 '''is plain mode active?
345
345
346 Plain mode means that all configuration variables which affect
346 Plain mode means that all configuration variables which affect
347 the behavior and output of Mercurial should be
347 the behavior and output of Mercurial should be
348 ignored. Additionally, the output should be stable,
348 ignored. Additionally, the output should be stable,
349 reproducible and suitable for use in scripts or applications.
349 reproducible and suitable for use in scripts or applications.
350
350
351 The only way to trigger plain mode is by setting either the
351 The only way to trigger plain mode is by setting either the
352 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
352 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
353
353
354 The return value can either be
354 The return value can either be
355 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
355 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
356 - True otherwise
356 - True otherwise
357 '''
357 '''
358 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
358 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
359 return False
359 return False
360 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
360 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
361 if feature and exceptions:
361 if feature and exceptions:
362 return feature not in exceptions
362 return feature not in exceptions
363 return True
363 return True
364
364
365 def username(self):
365 def username(self):
366 """Return default username to be used in commits.
366 """Return default username to be used in commits.
367
367
368 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
368 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
369 and stop searching if one of these is set.
369 and stop searching if one of these is set.
370 If not found and ui.askusername is True, ask the user, else use
370 If not found and ui.askusername is True, ask the user, else use
371 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
371 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
372 """
372 """
373 user = os.environ.get("HGUSER")
373 user = os.environ.get("HGUSER")
374 if user is None:
374 if user is None:
375 user = self.config("ui", "username")
375 user = self.config("ui", "username")
376 if user is not None:
376 if user is not None:
377 user = os.path.expandvars(user)
377 user = os.path.expandvars(user)
378 if user is None:
378 if user is None:
379 user = os.environ.get("EMAIL")
379 user = os.environ.get("EMAIL")
380 if user is None and self.configbool("ui", "askusername"):
380 if user is None and self.configbool("ui", "askusername"):
381 user = self.prompt(_("enter a commit username:"), default=None)
381 user = self.prompt(_("enter a commit username:"), default=None)
382 if user is None and not self.interactive():
382 if user is None and not self.interactive():
383 try:
383 try:
384 user = '%s@%s' % (util.getuser(), socket.getfqdn())
384 user = '%s@%s' % (util.getuser(), socket.getfqdn())
385 self.warn(_("No username found, using '%s' instead\n") % user)
385 self.warn(_("No username found, using '%s' instead\n") % user)
386 except KeyError:
386 except KeyError:
387 pass
387 pass
388 if not user:
388 if not user:
389 raise util.Abort(_('no username supplied (see "hg help config")'))
389 raise util.Abort(_('no username supplied (see "hg help config")'))
390 if "\n" in user:
390 if "\n" in user:
391 raise util.Abort(_("username %s contains a newline\n") % repr(user))
391 raise util.Abort(_("username %s contains a newline\n") % repr(user))
392 return user
392 return user
393
393
394 def shortuser(self, user):
394 def shortuser(self, user):
395 """Return a short representation of a user name or email address."""
395 """Return a short representation of a user name or email address."""
396 if not self.verbose:
396 if not self.verbose:
397 user = util.shortuser(user)
397 user = util.shortuser(user)
398 return user
398 return user
399
399
400 def expandpath(self, loc, default=None):
400 def expandpath(self, loc, default=None):
401 """Return repository location relative to cwd or from [paths]"""
401 """Return repository location relative to cwd or from [paths]"""
402 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
402 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
403 return loc
403 return loc
404
404
405 path = self.config('paths', loc)
405 path = self.config('paths', loc)
406 if not path and default is not None:
406 if not path and default is not None:
407 path = self.config('paths', default)
407 path = self.config('paths', default)
408 return path or loc
408 return path or loc
409
409
410 def pushbuffer(self):
410 def pushbuffer(self):
411 self._buffers.append([])
411 self._buffers.append([])
412
412
413 def popbuffer(self, labeled=False):
413 def popbuffer(self, labeled=False):
414 '''pop the last buffer and return the buffered output
414 '''pop the last buffer and return the buffered output
415
415
416 If labeled is True, any labels associated with buffered
416 If labeled is True, any labels associated with buffered
417 output will be handled. By default, this has no effect
417 output will be handled. By default, this has no effect
418 on the output returned, but extensions and GUI tools may
418 on the output returned, but extensions and GUI tools may
419 handle this argument and returned styled output. If output
419 handle this argument and returned styled output. If output
420 is being buffered so it can be captured and parsed or
420 is being buffered so it can be captured and parsed or
421 processed, labeled should not be set to True.
421 processed, labeled should not be set to True.
422 '''
422 '''
423 return "".join(self._buffers.pop())
423 return "".join(self._buffers.pop())
424
424
425 def write(self, *args, **opts):
425 def write(self, *args, **opts):
426 '''write args to output
426 '''write args to output
427
427
428 By default, this method simply writes to the buffer or stdout,
428 By default, this method simply writes to the buffer or stdout,
429 but extensions or GUI tools may override this method,
429 but extensions or GUI tools may override this method,
430 write_err(), popbuffer(), and label() to style output from
430 write_err(), popbuffer(), and label() to style output from
431 various parts of hg.
431 various parts of hg.
432
432
433 An optional keyword argument, "label", can be passed in.
433 An optional keyword argument, "label", can be passed in.
434 This should be a string containing label names separated by
434 This should be a string containing label names separated by
435 space. Label names take the form of "topic.type". For example,
435 space. Label names take the form of "topic.type". For example,
436 ui.debug() issues a label of "ui.debug".
436 ui.debug() issues a label of "ui.debug".
437
437
438 When labeling output for a specific command, a label of
438 When labeling output for a specific command, a label of
439 "cmdname.type" is recommended. For example, status issues
439 "cmdname.type" is recommended. For example, status issues
440 a label of "status.modified" for modified files.
440 a label of "status.modified" for modified files.
441 '''
441 '''
442 if self._buffers:
442 if self._buffers:
443 self._buffers[-1].extend([str(a) for a in args])
443 self._buffers[-1].extend([str(a) for a in args])
444 else:
444 else:
445 for a in args:
445 for a in args:
446 sys.stdout.write(str(a))
446 self.fout.write(str(a))
447
447
448 def write_err(self, *args, **opts):
448 def write_err(self, *args, **opts):
449 try:
449 try:
450 if not getattr(sys.stdout, 'closed', False):
450 if not getattr(self.fout, 'closed', False):
451 sys.stdout.flush()
451 self.fout.flush()
452 for a in args:
452 for a in args:
453 sys.stderr.write(str(a))
453 self.ferr.write(str(a))
454 # stderr may be buffered under win32 when redirected to files,
454 # stderr may be buffered under win32 when redirected to files,
455 # including stdout.
455 # including stdout.
456 if not getattr(sys.stderr, 'closed', False):
456 if not getattr(self.ferr, 'closed', False):
457 sys.stderr.flush()
457 self.ferr.flush()
458 except IOError, inst:
458 except IOError, inst:
459 if inst.errno not in (errno.EPIPE, errno.EIO):
459 if inst.errno not in (errno.EPIPE, errno.EIO):
460 raise
460 raise
461
461
462 def flush(self):
462 def flush(self):
463 try: sys.stdout.flush()
463 try: self.fout.flush()
464 except: pass
464 except: pass
465 try: sys.stderr.flush()
465 try: self.ferr.flush()
466 except: pass
466 except: pass
467
467
468 def interactive(self):
468 def interactive(self):
469 '''is interactive input allowed?
469 '''is interactive input allowed?
470
470
471 An interactive session is a session where input can be reasonably read
471 An interactive session is a session where input can be reasonably read
472 from `sys.stdin'. If this function returns false, any attempt to read
472 from `sys.stdin'. If this function returns false, any attempt to read
473 from stdin should fail with an error, unless a sensible default has been
473 from stdin should fail with an error, unless a sensible default has been
474 specified.
474 specified.
475
475
476 Interactiveness is triggered by the value of the `ui.interactive'
476 Interactiveness is triggered by the value of the `ui.interactive'
477 configuration variable or - if it is unset - when `sys.stdin' points
477 configuration variable or - if it is unset - when `sys.stdin' points
478 to a terminal device.
478 to a terminal device.
479
479
480 This function refers to input only; for output, see `ui.formatted()'.
480 This function refers to input only; for output, see `ui.formatted()'.
481 '''
481 '''
482 i = self.configbool("ui", "interactive", None)
482 i = self.configbool("ui", "interactive", None)
483 if i is None:
483 if i is None:
484 # some environments replace stdin without implementing isatty
484 # some environments replace stdin without implementing isatty
485 # usually those are non-interactive
485 # usually those are non-interactive
486 return util.isatty(sys.stdin)
486 return util.isatty(self.fin)
487
487
488 return i
488 return i
489
489
490 def termwidth(self):
490 def termwidth(self):
491 '''how wide is the terminal in columns?
491 '''how wide is the terminal in columns?
492 '''
492 '''
493 if 'COLUMNS' in os.environ:
493 if 'COLUMNS' in os.environ:
494 try:
494 try:
495 return int(os.environ['COLUMNS'])
495 return int(os.environ['COLUMNS'])
496 except ValueError:
496 except ValueError:
497 pass
497 pass
498 return util.termwidth()
498 return util.termwidth()
499
499
500 def formatted(self):
500 def formatted(self):
501 '''should formatted output be used?
501 '''should formatted output be used?
502
502
503 It is often desirable to format the output to suite the output medium.
503 It is often desirable to format the output to suite the output medium.
504 Examples of this are truncating long lines or colorizing messages.
504 Examples of this are truncating long lines or colorizing messages.
505 However, this is not often not desirable when piping output into other
505 However, this is not often not desirable when piping output into other
506 utilities, e.g. `grep'.
506 utilities, e.g. `grep'.
507
507
508 Formatted output is triggered by the value of the `ui.formatted'
508 Formatted output is triggered by the value of the `ui.formatted'
509 configuration variable or - if it is unset - when `sys.stdout' points
509 configuration variable or - if it is unset - when `sys.stdout' points
510 to a terminal device. Please note that `ui.formatted' should be
510 to a terminal device. Please note that `ui.formatted' should be
511 considered an implementation detail; it is not intended for use outside
511 considered an implementation detail; it is not intended for use outside
512 Mercurial or its extensions.
512 Mercurial or its extensions.
513
513
514 This function refers to output only; for input, see `ui.interactive()'.
514 This function refers to output only; for input, see `ui.interactive()'.
515 This function always returns false when in plain mode, see `ui.plain()'.
515 This function always returns false when in plain mode, see `ui.plain()'.
516 '''
516 '''
517 if self.plain():
517 if self.plain():
518 return False
518 return False
519
519
520 i = self.configbool("ui", "formatted", None)
520 i = self.configbool("ui", "formatted", None)
521 if i is None:
521 if i is None:
522 # some environments replace stdout without implementing isatty
522 # some environments replace stdout without implementing isatty
523 # usually those are non-interactive
523 # usually those are non-interactive
524 return util.isatty(sys.stdout)
524 return util.isatty(self.fout)
525
525
526 return i
526 return i
527
527
528 def _readline(self, prompt=''):
528 def _readline(self, prompt=''):
529 if util.isatty(sys.stdin):
529 if util.isatty(self.fin):
530 try:
530 try:
531 # magically add command line editing support, where
531 # magically add command line editing support, where
532 # available
532 # available
533 import readline
533 import readline
534 # force demandimport to really load the module
534 # force demandimport to really load the module
535 readline.read_history_file
535 readline.read_history_file
536 # windows sometimes raises something other than ImportError
536 # windows sometimes raises something other than ImportError
537 except Exception:
537 except Exception:
538 pass
538 pass
539
540 # instead of trying to emulate raw_input, swap our in/out
541 # with sys.stdin/out
542 old = sys.stdout, sys.stdin
543 sys.stdout, sys.stdin = self.fout, self.fin
539 line = raw_input(prompt)
544 line = raw_input(prompt)
545 sys.stdout, sys.stdin = old
546
540 # When stdin is in binary mode on Windows, it can cause
547 # When stdin is in binary mode on Windows, it can cause
541 # raw_input() to emit an extra trailing carriage return
548 # raw_input() to emit an extra trailing carriage return
542 if os.linesep == '\r\n' and line and line[-1] == '\r':
549 if os.linesep == '\r\n' and line and line[-1] == '\r':
543 line = line[:-1]
550 line = line[:-1]
544 return line
551 return line
545
552
546 def prompt(self, msg, default="y"):
553 def prompt(self, msg, default="y"):
547 """Prompt user with msg, read response.
554 """Prompt user with msg, read response.
548 If ui is not interactive, the default is returned.
555 If ui is not interactive, the default is returned.
549 """
556 """
550 if not self.interactive():
557 if not self.interactive():
551 self.write(msg, ' ', default, "\n")
558 self.write(msg, ' ', default, "\n")
552 return default
559 return default
553 try:
560 try:
554 r = self._readline(self.label(msg, 'ui.prompt') + ' ')
561 r = self._readline(self.label(msg, 'ui.prompt') + ' ')
555 if not r:
562 if not r:
556 return default
563 return default
557 return r
564 return r
558 except EOFError:
565 except EOFError:
559 raise util.Abort(_('response expected'))
566 raise util.Abort(_('response expected'))
560
567
561 def promptchoice(self, msg, choices, default=0):
568 def promptchoice(self, msg, choices, default=0):
562 """Prompt user with msg, read response, and ensure it matches
569 """Prompt user with msg, read response, and ensure it matches
563 one of the provided choices. The index of the choice is returned.
570 one of the provided choices. The index of the choice is returned.
564 choices is a sequence of acceptable responses with the format:
571 choices is a sequence of acceptable responses with the format:
565 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
572 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
566 If ui is not interactive, the default is returned.
573 If ui is not interactive, the default is returned.
567 """
574 """
568 resps = [s[s.index('&')+1].lower() for s in choices]
575 resps = [s[s.index('&')+1].lower() for s in choices]
569 while True:
576 while True:
570 r = self.prompt(msg, resps[default])
577 r = self.prompt(msg, resps[default])
571 if r.lower() in resps:
578 if r.lower() in resps:
572 return resps.index(r.lower())
579 return resps.index(r.lower())
573 self.write(_("unrecognized response\n"))
580 self.write(_("unrecognized response\n"))
574
581
575 def getpass(self, prompt=None, default=None):
582 def getpass(self, prompt=None, default=None):
576 if not self.interactive():
583 if not self.interactive():
577 return default
584 return default
578 try:
585 try:
579 return getpass.getpass(prompt or _('password: '))
586 return getpass.getpass(prompt or _('password: '))
580 except EOFError:
587 except EOFError:
581 raise util.Abort(_('response expected'))
588 raise util.Abort(_('response expected'))
582 def status(self, *msg, **opts):
589 def status(self, *msg, **opts):
583 '''write status message to output (if ui.quiet is False)
590 '''write status message to output (if ui.quiet is False)
584
591
585 This adds an output label of "ui.status".
592 This adds an output label of "ui.status".
586 '''
593 '''
587 if not self.quiet:
594 if not self.quiet:
588 opts['label'] = opts.get('label', '') + ' ui.status'
595 opts['label'] = opts.get('label', '') + ' ui.status'
589 self.write(*msg, **opts)
596 self.write(*msg, **opts)
590 def warn(self, *msg, **opts):
597 def warn(self, *msg, **opts):
591 '''write warning message to output (stderr)
598 '''write warning message to output (stderr)
592
599
593 This adds an output label of "ui.warning".
600 This adds an output label of "ui.warning".
594 '''
601 '''
595 opts['label'] = opts.get('label', '') + ' ui.warning'
602 opts['label'] = opts.get('label', '') + ' ui.warning'
596 self.write_err(*msg, **opts)
603 self.write_err(*msg, **opts)
597 def note(self, *msg, **opts):
604 def note(self, *msg, **opts):
598 '''write note to output (if ui.verbose is True)
605 '''write note to output (if ui.verbose is True)
599
606
600 This adds an output label of "ui.note".
607 This adds an output label of "ui.note".
601 '''
608 '''
602 if self.verbose:
609 if self.verbose:
603 opts['label'] = opts.get('label', '') + ' ui.note'
610 opts['label'] = opts.get('label', '') + ' ui.note'
604 self.write(*msg, **opts)
611 self.write(*msg, **opts)
605 def debug(self, *msg, **opts):
612 def debug(self, *msg, **opts):
606 '''write debug message to output (if ui.debugflag is True)
613 '''write debug message to output (if ui.debugflag is True)
607
614
608 This adds an output label of "ui.debug".
615 This adds an output label of "ui.debug".
609 '''
616 '''
610 if self.debugflag:
617 if self.debugflag:
611 opts['label'] = opts.get('label', '') + ' ui.debug'
618 opts['label'] = opts.get('label', '') + ' ui.debug'
612 self.write(*msg, **opts)
619 self.write(*msg, **opts)
613 def edit(self, text, user):
620 def edit(self, text, user):
614 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
621 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
615 text=True)
622 text=True)
616 try:
623 try:
617 f = os.fdopen(fd, "w")
624 f = os.fdopen(fd, "w")
618 f.write(text)
625 f.write(text)
619 f.close()
626 f.close()
620
627
621 editor = self.geteditor()
628 editor = self.geteditor()
622
629
623 util.system("%s \"%s\"" % (editor, name),
630 util.system("%s \"%s\"" % (editor, name),
624 environ={'HGUSER': user},
631 environ={'HGUSER': user},
625 onerr=util.Abort, errprefix=_("edit failed"))
632 onerr=util.Abort, errprefix=_("edit failed"))
626
633
627 f = open(name)
634 f = open(name)
628 t = f.read()
635 t = f.read()
629 f.close()
636 f.close()
630 finally:
637 finally:
631 os.unlink(name)
638 os.unlink(name)
632
639
633 return t
640 return t
634
641
635 def traceback(self, exc=None):
642 def traceback(self, exc=None):
636 '''print exception traceback if traceback printing enabled.
643 '''print exception traceback if traceback printing enabled.
637 only to call in exception handler. returns true if traceback
644 only to call in exception handler. returns true if traceback
638 printed.'''
645 printed.'''
639 if self.tracebackflag:
646 if self.tracebackflag:
640 if exc:
647 if exc:
641 traceback.print_exception(exc[0], exc[1], exc[2])
648 traceback.print_exception(exc[0], exc[1], exc[2])
642 else:
649 else:
643 traceback.print_exc()
650 traceback.print_exc()
644 return self.tracebackflag
651 return self.tracebackflag
645
652
646 def geteditor(self):
653 def geteditor(self):
647 '''return editor to use'''
654 '''return editor to use'''
648 return (os.environ.get("HGEDITOR") or
655 return (os.environ.get("HGEDITOR") or
649 self.config("ui", "editor") or
656 self.config("ui", "editor") or
650 os.environ.get("VISUAL") or
657 os.environ.get("VISUAL") or
651 os.environ.get("EDITOR", "vi"))
658 os.environ.get("EDITOR", "vi"))
652
659
653 def progress(self, topic, pos, item="", unit="", total=None):
660 def progress(self, topic, pos, item="", unit="", total=None):
654 '''show a progress message
661 '''show a progress message
655
662
656 With stock hg, this is simply a debug message that is hidden
663 With stock hg, this is simply a debug message that is hidden
657 by default, but with extensions or GUI tools it may be
664 by default, but with extensions or GUI tools it may be
658 visible. 'topic' is the current operation, 'item' is a
665 visible. 'topic' is the current operation, 'item' is a
659 non-numeric marker of the current position (ie the currently
666 non-numeric marker of the current position (ie the currently
660 in-process file), 'pos' is the current numeric position (ie
667 in-process file), 'pos' is the current numeric position (ie
661 revision, bytes, etc.), unit is a corresponding unit label,
668 revision, bytes, etc.), unit is a corresponding unit label,
662 and total is the highest expected pos.
669 and total is the highest expected pos.
663
670
664 Multiple nested topics may be active at a time.
671 Multiple nested topics may be active at a time.
665
672
666 All topics should be marked closed by setting pos to None at
673 All topics should be marked closed by setting pos to None at
667 termination.
674 termination.
668 '''
675 '''
669
676
670 if pos is None or not self.debugflag:
677 if pos is None or not self.debugflag:
671 return
678 return
672
679
673 if unit:
680 if unit:
674 unit = ' ' + unit
681 unit = ' ' + unit
675 if item:
682 if item:
676 item = ' ' + item
683 item = ' ' + item
677
684
678 if total:
685 if total:
679 pct = 100.0 * pos / total
686 pct = 100.0 * pos / total
680 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
687 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
681 % (topic, item, pos, total, unit, pct))
688 % (topic, item, pos, total, unit, pct))
682 else:
689 else:
683 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
690 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
684
691
685 def log(self, service, message):
692 def log(self, service, message):
686 '''hook for logging facility extensions
693 '''hook for logging facility extensions
687
694
688 service should be a readily-identifiable subsystem, which will
695 service should be a readily-identifiable subsystem, which will
689 allow filtering.
696 allow filtering.
690 message should be a newline-terminated string to log.
697 message should be a newline-terminated string to log.
691 '''
698 '''
692 pass
699 pass
693
700
694 def label(self, msg, label):
701 def label(self, msg, label):
695 '''style msg based on supplied label
702 '''style msg based on supplied label
696
703
697 Like ui.write(), this just returns msg unchanged, but extensions
704 Like ui.write(), this just returns msg unchanged, but extensions
698 and GUI tools can override it to allow styling output without
705 and GUI tools can override it to allow styling output without
699 writing it.
706 writing it.
700
707
701 ui.write(s, 'label') is equivalent to
708 ui.write(s, 'label') is equivalent to
702 ui.write(ui.label(s, 'label')).
709 ui.write(ui.label(s, 'label')).
703 '''
710 '''
704 return msg
711 return msg
@@ -1,33 +1,33
1 import os, sys
1 import os, sys
2 from hgext import color
2 from hgext import color
3 from mercurial import dispatch, ui
3 from mercurial import dispatch, ui
4
4
5 # ensure errors aren't buffered
5 # ensure errors aren't buffered
6 testui = color.colorui()
6 testui = color.colorui()
7 testui.pushbuffer()
7 testui.pushbuffer()
8 testui.write('buffered\n')
8 testui.write('buffered\n')
9 testui.warn('warning\n')
9 testui.warn('warning\n')
10 testui.write_err('error\n')
10 testui.write_err('error\n')
11 print repr(testui.popbuffer())
11 print repr(testui.popbuffer())
12
12
13 # test dispatch.dispatch with the same ui object
13 # test dispatch.dispatch with the same ui object
14 hgrc = open(os.environ["HGRCPATH"], 'w')
14 hgrc = open(os.environ["HGRCPATH"], 'w')
15 hgrc.write('[extensions]\n')
15 hgrc.write('[extensions]\n')
16 hgrc.write('color=\n')
16 hgrc.write('color=\n')
17 hgrc.close()
17 hgrc.close()
18
18
19 ui_ = ui.ui()
19 ui_ = ui.ui()
20 ui_.setconfig('ui', 'formatted', 'True')
20 ui_.setconfig('ui', 'formatted', 'True')
21
21
22 # we're not interested in the output, so write that to devnull
23 ui_.fout = open(os.devnull, 'w')
24
22 # call some arbitrary command just so we go through
25 # call some arbitrary command just so we go through
23 # color's wrapped _runcommand twice.
26 # color's wrapped _runcommand twice.
24 # we're not interested in the output, so write that to devnull
25 def runcmd():
27 def runcmd():
26 sys.stdout = open(os.devnull, 'w')
27 dispatch.dispatch(dispatch.request(['version', '-q'], ui_))
28 dispatch.dispatch(dispatch.request(['version', '-q'], ui_))
28 sys.stdout = sys.__stdout__
29
29
30 runcmd()
30 runcmd()
31 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
31 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
32 runcmd()
32 runcmd()
33 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
33 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
General Comments 0
You need to be logged in to leave comments. Login now