##// END OF EJS Templates
ui: send password prompts to stderr again (issue4056)
Matt Mackall -
r19880:ba2be32d stable
parent child Browse files
Show More
@@ -1,814 +1,814 b''
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, formatter
10 import config, scmutil, util, error, formatter
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 self.callhooks = True
22 self.callhooks = True
23
23
24 if src:
24 if src:
25 self.fout = src.fout
25 self.fout = src.fout
26 self.ferr = src.ferr
26 self.ferr = src.ferr
27 self.fin = src.fin
27 self.fin = src.fin
28
28
29 self._tcfg = src._tcfg.copy()
29 self._tcfg = src._tcfg.copy()
30 self._ucfg = src._ucfg.copy()
30 self._ucfg = src._ucfg.copy()
31 self._ocfg = src._ocfg.copy()
31 self._ocfg = src._ocfg.copy()
32 self._trustusers = src._trustusers.copy()
32 self._trustusers = src._trustusers.copy()
33 self._trustgroups = src._trustgroups.copy()
33 self._trustgroups = src._trustgroups.copy()
34 self.environ = src.environ
34 self.environ = src.environ
35 self.callhooks = src.callhooks
35 self.callhooks = src.callhooks
36 self.fixconfig()
36 self.fixconfig()
37 else:
37 else:
38 self.fout = sys.stdout
38 self.fout = sys.stdout
39 self.ferr = sys.stderr
39 self.ferr = sys.stderr
40 self.fin = sys.stdin
40 self.fin = sys.stdin
41
41
42 # shared read-only environment
42 # shared read-only environment
43 self.environ = os.environ
43 self.environ = os.environ
44 # we always trust global config files
44 # we always trust global config files
45 for f in scmutil.rcpath():
45 for f in scmutil.rcpath():
46 self.readconfig(f, trust=True)
46 self.readconfig(f, trust=True)
47
47
48 def copy(self):
48 def copy(self):
49 return self.__class__(self)
49 return self.__class__(self)
50
50
51 def formatter(self, topic, opts):
51 def formatter(self, topic, opts):
52 return formatter.formatter(self, topic, opts)
52 return formatter.formatter(self, topic, opts)
53
53
54 def _trusted(self, fp, f):
54 def _trusted(self, fp, f):
55 st = util.fstat(fp)
55 st = util.fstat(fp)
56 if util.isowner(st):
56 if util.isowner(st):
57 return True
57 return True
58
58
59 tusers, tgroups = self._trustusers, self._trustgroups
59 tusers, tgroups = self._trustusers, self._trustgroups
60 if '*' in tusers or '*' in tgroups:
60 if '*' in tusers or '*' in tgroups:
61 return True
61 return True
62
62
63 user = util.username(st.st_uid)
63 user = util.username(st.st_uid)
64 group = util.groupname(st.st_gid)
64 group = util.groupname(st.st_gid)
65 if user in tusers or group in tgroups or user == util.username():
65 if user in tusers or group in tgroups or user == util.username():
66 return True
66 return True
67
67
68 if self._reportuntrusted:
68 if self._reportuntrusted:
69 self.warn(_('not trusting file %s from untrusted '
69 self.warn(_('not trusting file %s from untrusted '
70 'user %s, group %s\n') % (f, user, group))
70 'user %s, group %s\n') % (f, user, group))
71 return False
71 return False
72
72
73 def readconfig(self, filename, root=None, trust=False,
73 def readconfig(self, filename, root=None, trust=False,
74 sections=None, remap=None):
74 sections=None, remap=None):
75 try:
75 try:
76 fp = open(filename)
76 fp = open(filename)
77 except IOError:
77 except IOError:
78 if not sections: # ignore unless we were looking for something
78 if not sections: # ignore unless we were looking for something
79 return
79 return
80 raise
80 raise
81
81
82 cfg = config.config()
82 cfg = config.config()
83 trusted = sections or trust or self._trusted(fp, filename)
83 trusted = sections or trust or self._trusted(fp, filename)
84
84
85 try:
85 try:
86 cfg.read(filename, fp, sections=sections, remap=remap)
86 cfg.read(filename, fp, sections=sections, remap=remap)
87 fp.close()
87 fp.close()
88 except error.ConfigError, inst:
88 except error.ConfigError, inst:
89 if trusted:
89 if trusted:
90 raise
90 raise
91 self.warn(_("ignored: %s\n") % str(inst))
91 self.warn(_("ignored: %s\n") % str(inst))
92
92
93 if self.plain():
93 if self.plain():
94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
95 'logtemplate', 'style',
95 'logtemplate', 'style',
96 'traceback', 'verbose'):
96 'traceback', 'verbose'):
97 if k in cfg['ui']:
97 if k in cfg['ui']:
98 del cfg['ui'][k]
98 del cfg['ui'][k]
99 for k, v in cfg.items('defaults'):
99 for k, v in cfg.items('defaults'):
100 del cfg['defaults'][k]
100 del cfg['defaults'][k]
101 # Don't remove aliases from the configuration if in the exceptionlist
101 # Don't remove aliases from the configuration if in the exceptionlist
102 if self.plain('alias'):
102 if self.plain('alias'):
103 for k, v in cfg.items('alias'):
103 for k, v in cfg.items('alias'):
104 del cfg['alias'][k]
104 del cfg['alias'][k]
105
105
106 if trusted:
106 if trusted:
107 self._tcfg.update(cfg)
107 self._tcfg.update(cfg)
108 self._tcfg.update(self._ocfg)
108 self._tcfg.update(self._ocfg)
109 self._ucfg.update(cfg)
109 self._ucfg.update(cfg)
110 self._ucfg.update(self._ocfg)
110 self._ucfg.update(self._ocfg)
111
111
112 if root is None:
112 if root is None:
113 root = os.path.expanduser('~')
113 root = os.path.expanduser('~')
114 self.fixconfig(root=root)
114 self.fixconfig(root=root)
115
115
116 def fixconfig(self, root=None, section=None):
116 def fixconfig(self, root=None, section=None):
117 if section in (None, 'paths'):
117 if section in (None, 'paths'):
118 # expand vars and ~
118 # expand vars and ~
119 # translate paths relative to root (or home) into absolute paths
119 # translate paths relative to root (or home) into absolute paths
120 root = root or os.getcwd()
120 root = root or os.getcwd()
121 for c in self._tcfg, self._ucfg, self._ocfg:
121 for c in self._tcfg, self._ucfg, self._ocfg:
122 for n, p in c.items('paths'):
122 for n, p in c.items('paths'):
123 if not p:
123 if not p:
124 continue
124 continue
125 if '%%' in p:
125 if '%%' in p:
126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
127 % (n, p, self.configsource('paths', n)))
127 % (n, p, self.configsource('paths', n)))
128 p = p.replace('%%', '%')
128 p = p.replace('%%', '%')
129 p = util.expandpath(p)
129 p = util.expandpath(p)
130 if not util.hasscheme(p) and not os.path.isabs(p):
130 if not util.hasscheme(p) and not os.path.isabs(p):
131 p = os.path.normpath(os.path.join(root, p))
131 p = os.path.normpath(os.path.join(root, p))
132 c.set("paths", n, p)
132 c.set("paths", n, p)
133
133
134 if section in (None, 'ui'):
134 if section in (None, 'ui'):
135 # update ui options
135 # update ui options
136 self.debugflag = self.configbool('ui', 'debug')
136 self.debugflag = self.configbool('ui', 'debug')
137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
139 if self.verbose and self.quiet:
139 if self.verbose and self.quiet:
140 self.quiet = self.verbose = False
140 self.quiet = self.verbose = False
141 self._reportuntrusted = self.debugflag or self.configbool("ui",
141 self._reportuntrusted = self.debugflag or self.configbool("ui",
142 "report_untrusted", True)
142 "report_untrusted", True)
143 self.tracebackflag = self.configbool('ui', 'traceback', False)
143 self.tracebackflag = self.configbool('ui', 'traceback', False)
144
144
145 if section in (None, 'trusted'):
145 if section in (None, 'trusted'):
146 # update trust information
146 # update trust information
147 self._trustusers.update(self.configlist('trusted', 'users'))
147 self._trustusers.update(self.configlist('trusted', 'users'))
148 self._trustgroups.update(self.configlist('trusted', 'groups'))
148 self._trustgroups.update(self.configlist('trusted', 'groups'))
149
149
150 def backupconfig(self, section, item):
150 def backupconfig(self, section, item):
151 return (self._ocfg.backup(section, item),
151 return (self._ocfg.backup(section, item),
152 self._tcfg.backup(section, item),
152 self._tcfg.backup(section, item),
153 self._ucfg.backup(section, item),)
153 self._ucfg.backup(section, item),)
154 def restoreconfig(self, data):
154 def restoreconfig(self, data):
155 self._ocfg.restore(data[0])
155 self._ocfg.restore(data[0])
156 self._tcfg.restore(data[1])
156 self._tcfg.restore(data[1])
157 self._ucfg.restore(data[2])
157 self._ucfg.restore(data[2])
158
158
159 def setconfig(self, section, name, value, overlay=True):
159 def setconfig(self, section, name, value, overlay=True):
160 if overlay:
160 if overlay:
161 self._ocfg.set(section, name, value)
161 self._ocfg.set(section, name, value)
162 self._tcfg.set(section, name, value)
162 self._tcfg.set(section, name, value)
163 self._ucfg.set(section, name, value)
163 self._ucfg.set(section, name, value)
164 self.fixconfig(section=section)
164 self.fixconfig(section=section)
165
165
166 def _data(self, untrusted):
166 def _data(self, untrusted):
167 return untrusted and self._ucfg or self._tcfg
167 return untrusted and self._ucfg or self._tcfg
168
168
169 def configsource(self, section, name, untrusted=False):
169 def configsource(self, section, name, untrusted=False):
170 return self._data(untrusted).source(section, name) or 'none'
170 return self._data(untrusted).source(section, name) or 'none'
171
171
172 def config(self, section, name, default=None, untrusted=False):
172 def config(self, section, name, default=None, untrusted=False):
173 if isinstance(name, list):
173 if isinstance(name, list):
174 alternates = name
174 alternates = name
175 else:
175 else:
176 alternates = [name]
176 alternates = [name]
177
177
178 for n in alternates:
178 for n in alternates:
179 value = self._data(untrusted).get(section, name, None)
179 value = self._data(untrusted).get(section, name, None)
180 if value is not None:
180 if value is not None:
181 name = n
181 name = n
182 break
182 break
183 else:
183 else:
184 value = default
184 value = default
185
185
186 if self.debugflag and not untrusted and self._reportuntrusted:
186 if self.debugflag and not untrusted and self._reportuntrusted:
187 uvalue = self._ucfg.get(section, name)
187 uvalue = self._ucfg.get(section, name)
188 if uvalue is not None and uvalue != value:
188 if uvalue is not None and uvalue != value:
189 self.debug("ignoring untrusted configuration option "
189 self.debug("ignoring untrusted configuration option "
190 "%s.%s = %s\n" % (section, name, uvalue))
190 "%s.%s = %s\n" % (section, name, uvalue))
191 return value
191 return value
192
192
193 def configpath(self, section, name, default=None, untrusted=False):
193 def configpath(self, section, name, default=None, untrusted=False):
194 'get a path config item, expanded relative to repo root or config file'
194 'get a path config item, expanded relative to repo root or config file'
195 v = self.config(section, name, default, untrusted)
195 v = self.config(section, name, default, untrusted)
196 if v is None:
196 if v is None:
197 return None
197 return None
198 if not os.path.isabs(v) or "://" not in v:
198 if not os.path.isabs(v) or "://" not in v:
199 src = self.configsource(section, name, untrusted)
199 src = self.configsource(section, name, untrusted)
200 if ':' in src:
200 if ':' in src:
201 base = os.path.dirname(src.rsplit(':')[0])
201 base = os.path.dirname(src.rsplit(':')[0])
202 v = os.path.join(base, os.path.expanduser(v))
202 v = os.path.join(base, os.path.expanduser(v))
203 return v
203 return v
204
204
205 def configbool(self, section, name, default=False, untrusted=False):
205 def configbool(self, section, name, default=False, untrusted=False):
206 """parse a configuration element as a boolean
206 """parse a configuration element as a boolean
207
207
208 >>> u = ui(); s = 'foo'
208 >>> u = ui(); s = 'foo'
209 >>> u.setconfig(s, 'true', 'yes')
209 >>> u.setconfig(s, 'true', 'yes')
210 >>> u.configbool(s, 'true')
210 >>> u.configbool(s, 'true')
211 True
211 True
212 >>> u.setconfig(s, 'false', 'no')
212 >>> u.setconfig(s, 'false', 'no')
213 >>> u.configbool(s, 'false')
213 >>> u.configbool(s, 'false')
214 False
214 False
215 >>> u.configbool(s, 'unknown')
215 >>> u.configbool(s, 'unknown')
216 False
216 False
217 >>> u.configbool(s, 'unknown', True)
217 >>> u.configbool(s, 'unknown', True)
218 True
218 True
219 >>> u.setconfig(s, 'invalid', 'somevalue')
219 >>> u.setconfig(s, 'invalid', 'somevalue')
220 >>> u.configbool(s, 'invalid')
220 >>> u.configbool(s, 'invalid')
221 Traceback (most recent call last):
221 Traceback (most recent call last):
222 ...
222 ...
223 ConfigError: foo.invalid is not a boolean ('somevalue')
223 ConfigError: foo.invalid is not a boolean ('somevalue')
224 """
224 """
225
225
226 v = self.config(section, name, None, untrusted)
226 v = self.config(section, name, None, untrusted)
227 if v is None:
227 if v is None:
228 return default
228 return default
229 if isinstance(v, bool):
229 if isinstance(v, bool):
230 return v
230 return v
231 b = util.parsebool(v)
231 b = util.parsebool(v)
232 if b is None:
232 if b is None:
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
234 % (section, name, v))
234 % (section, name, v))
235 return b
235 return b
236
236
237 def configint(self, section, name, default=None, untrusted=False):
237 def configint(self, section, name, default=None, untrusted=False):
238 """parse a configuration element as an integer
238 """parse a configuration element as an integer
239
239
240 >>> u = ui(); s = 'foo'
240 >>> u = ui(); s = 'foo'
241 >>> u.setconfig(s, 'int1', '42')
241 >>> u.setconfig(s, 'int1', '42')
242 >>> u.configint(s, 'int1')
242 >>> u.configint(s, 'int1')
243 42
243 42
244 >>> u.setconfig(s, 'int2', '-42')
244 >>> u.setconfig(s, 'int2', '-42')
245 >>> u.configint(s, 'int2')
245 >>> u.configint(s, 'int2')
246 -42
246 -42
247 >>> u.configint(s, 'unknown', 7)
247 >>> u.configint(s, 'unknown', 7)
248 7
248 7
249 >>> u.setconfig(s, 'invalid', 'somevalue')
249 >>> u.setconfig(s, 'invalid', 'somevalue')
250 >>> u.configint(s, 'invalid')
250 >>> u.configint(s, 'invalid')
251 Traceback (most recent call last):
251 Traceback (most recent call last):
252 ...
252 ...
253 ConfigError: foo.invalid is not an integer ('somevalue')
253 ConfigError: foo.invalid is not an integer ('somevalue')
254 """
254 """
255
255
256 v = self.config(section, name, None, untrusted)
256 v = self.config(section, name, None, untrusted)
257 if v is None:
257 if v is None:
258 return default
258 return default
259 try:
259 try:
260 return int(v)
260 return int(v)
261 except ValueError:
261 except ValueError:
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
263 % (section, name, v))
263 % (section, name, v))
264
264
265 def configbytes(self, section, name, default=0, untrusted=False):
265 def configbytes(self, section, name, default=0, untrusted=False):
266 """parse a configuration element as a quantity in bytes
266 """parse a configuration element as a quantity in bytes
267
267
268 Units can be specified as b (bytes), k or kb (kilobytes), m or
268 Units can be specified as b (bytes), k or kb (kilobytes), m or
269 mb (megabytes), g or gb (gigabytes).
269 mb (megabytes), g or gb (gigabytes).
270
270
271 >>> u = ui(); s = 'foo'
271 >>> u = ui(); s = 'foo'
272 >>> u.setconfig(s, 'val1', '42')
272 >>> u.setconfig(s, 'val1', '42')
273 >>> u.configbytes(s, 'val1')
273 >>> u.configbytes(s, 'val1')
274 42
274 42
275 >>> u.setconfig(s, 'val2', '42.5 kb')
275 >>> u.setconfig(s, 'val2', '42.5 kb')
276 >>> u.configbytes(s, 'val2')
276 >>> u.configbytes(s, 'val2')
277 43520
277 43520
278 >>> u.configbytes(s, 'unknown', '7 MB')
278 >>> u.configbytes(s, 'unknown', '7 MB')
279 7340032
279 7340032
280 >>> u.setconfig(s, 'invalid', 'somevalue')
280 >>> u.setconfig(s, 'invalid', 'somevalue')
281 >>> u.configbytes(s, 'invalid')
281 >>> u.configbytes(s, 'invalid')
282 Traceback (most recent call last):
282 Traceback (most recent call last):
283 ...
283 ...
284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
285 """
285 """
286
286
287 value = self.config(section, name)
287 value = self.config(section, name)
288 if value is None:
288 if value is None:
289 if not isinstance(default, str):
289 if not isinstance(default, str):
290 return default
290 return default
291 value = default
291 value = default
292 try:
292 try:
293 return util.sizetoint(value)
293 return util.sizetoint(value)
294 except error.ParseError:
294 except error.ParseError:
295 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
295 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
296 % (section, name, value))
296 % (section, name, value))
297
297
298 def configlist(self, section, name, default=None, untrusted=False):
298 def configlist(self, section, name, default=None, untrusted=False):
299 """parse a configuration element as a list of comma/space separated
299 """parse a configuration element as a list of comma/space separated
300 strings
300 strings
301
301
302 >>> u = ui(); s = 'foo'
302 >>> u = ui(); s = 'foo'
303 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
303 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
304 >>> u.configlist(s, 'list1')
304 >>> u.configlist(s, 'list1')
305 ['this', 'is', 'a small', 'test']
305 ['this', 'is', 'a small', 'test']
306 """
306 """
307
307
308 def _parse_plain(parts, s, offset):
308 def _parse_plain(parts, s, offset):
309 whitespace = False
309 whitespace = False
310 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
310 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
311 whitespace = True
311 whitespace = True
312 offset += 1
312 offset += 1
313 if offset >= len(s):
313 if offset >= len(s):
314 return None, parts, offset
314 return None, parts, offset
315 if whitespace:
315 if whitespace:
316 parts.append('')
316 parts.append('')
317 if s[offset] == '"' and not parts[-1]:
317 if s[offset] == '"' and not parts[-1]:
318 return _parse_quote, parts, offset + 1
318 return _parse_quote, parts, offset + 1
319 elif s[offset] == '"' and parts[-1][-1] == '\\':
319 elif s[offset] == '"' and parts[-1][-1] == '\\':
320 parts[-1] = parts[-1][:-1] + s[offset]
320 parts[-1] = parts[-1][:-1] + s[offset]
321 return _parse_plain, parts, offset + 1
321 return _parse_plain, parts, offset + 1
322 parts[-1] += s[offset]
322 parts[-1] += s[offset]
323 return _parse_plain, parts, offset + 1
323 return _parse_plain, parts, offset + 1
324
324
325 def _parse_quote(parts, s, offset):
325 def _parse_quote(parts, s, offset):
326 if offset < len(s) and s[offset] == '"': # ""
326 if offset < len(s) and s[offset] == '"': # ""
327 parts.append('')
327 parts.append('')
328 offset += 1
328 offset += 1
329 while offset < len(s) and (s[offset].isspace() or
329 while offset < len(s) and (s[offset].isspace() or
330 s[offset] == ','):
330 s[offset] == ','):
331 offset += 1
331 offset += 1
332 return _parse_plain, parts, offset
332 return _parse_plain, parts, offset
333
333
334 while offset < len(s) and s[offset] != '"':
334 while offset < len(s) and s[offset] != '"':
335 if (s[offset] == '\\' and offset + 1 < len(s)
335 if (s[offset] == '\\' and offset + 1 < len(s)
336 and s[offset + 1] == '"'):
336 and s[offset + 1] == '"'):
337 offset += 1
337 offset += 1
338 parts[-1] += '"'
338 parts[-1] += '"'
339 else:
339 else:
340 parts[-1] += s[offset]
340 parts[-1] += s[offset]
341 offset += 1
341 offset += 1
342
342
343 if offset >= len(s):
343 if offset >= len(s):
344 real_parts = _configlist(parts[-1])
344 real_parts = _configlist(parts[-1])
345 if not real_parts:
345 if not real_parts:
346 parts[-1] = '"'
346 parts[-1] = '"'
347 else:
347 else:
348 real_parts[0] = '"' + real_parts[0]
348 real_parts[0] = '"' + real_parts[0]
349 parts = parts[:-1]
349 parts = parts[:-1]
350 parts.extend(real_parts)
350 parts.extend(real_parts)
351 return None, parts, offset
351 return None, parts, offset
352
352
353 offset += 1
353 offset += 1
354 while offset < len(s) and s[offset] in [' ', ',']:
354 while offset < len(s) and s[offset] in [' ', ',']:
355 offset += 1
355 offset += 1
356
356
357 if offset < len(s):
357 if offset < len(s):
358 if offset + 1 == len(s) and s[offset] == '"':
358 if offset + 1 == len(s) and s[offset] == '"':
359 parts[-1] += '"'
359 parts[-1] += '"'
360 offset += 1
360 offset += 1
361 else:
361 else:
362 parts.append('')
362 parts.append('')
363 else:
363 else:
364 return None, parts, offset
364 return None, parts, offset
365
365
366 return _parse_plain, parts, offset
366 return _parse_plain, parts, offset
367
367
368 def _configlist(s):
368 def _configlist(s):
369 s = s.rstrip(' ,')
369 s = s.rstrip(' ,')
370 if not s:
370 if not s:
371 return []
371 return []
372 parser, parts, offset = _parse_plain, [''], 0
372 parser, parts, offset = _parse_plain, [''], 0
373 while parser:
373 while parser:
374 parser, parts, offset = parser(parts, s, offset)
374 parser, parts, offset = parser(parts, s, offset)
375 return parts
375 return parts
376
376
377 result = self.config(section, name, untrusted=untrusted)
377 result = self.config(section, name, untrusted=untrusted)
378 if result is None:
378 if result is None:
379 result = default or []
379 result = default or []
380 if isinstance(result, basestring):
380 if isinstance(result, basestring):
381 result = _configlist(result.lstrip(' ,\n'))
381 result = _configlist(result.lstrip(' ,\n'))
382 if result is None:
382 if result is None:
383 result = default or []
383 result = default or []
384 return result
384 return result
385
385
386 def has_section(self, section, untrusted=False):
386 def has_section(self, section, untrusted=False):
387 '''tell whether section exists in config.'''
387 '''tell whether section exists in config.'''
388 return section in self._data(untrusted)
388 return section in self._data(untrusted)
389
389
390 def configitems(self, section, untrusted=False):
390 def configitems(self, section, untrusted=False):
391 items = self._data(untrusted).items(section)
391 items = self._data(untrusted).items(section)
392 if self.debugflag and not untrusted and self._reportuntrusted:
392 if self.debugflag and not untrusted and self._reportuntrusted:
393 for k, v in self._ucfg.items(section):
393 for k, v in self._ucfg.items(section):
394 if self._tcfg.get(section, k) != v:
394 if self._tcfg.get(section, k) != v:
395 self.debug("ignoring untrusted configuration option "
395 self.debug("ignoring untrusted configuration option "
396 "%s.%s = %s\n" % (section, k, v))
396 "%s.%s = %s\n" % (section, k, v))
397 return items
397 return items
398
398
399 def walkconfig(self, untrusted=False):
399 def walkconfig(self, untrusted=False):
400 cfg = self._data(untrusted)
400 cfg = self._data(untrusted)
401 for section in cfg.sections():
401 for section in cfg.sections():
402 for name, value in self.configitems(section, untrusted):
402 for name, value in self.configitems(section, untrusted):
403 yield section, name, value
403 yield section, name, value
404
404
405 def plain(self, feature=None):
405 def plain(self, feature=None):
406 '''is plain mode active?
406 '''is plain mode active?
407
407
408 Plain mode means that all configuration variables which affect
408 Plain mode means that all configuration variables which affect
409 the behavior and output of Mercurial should be
409 the behavior and output of Mercurial should be
410 ignored. Additionally, the output should be stable,
410 ignored. Additionally, the output should be stable,
411 reproducible and suitable for use in scripts or applications.
411 reproducible and suitable for use in scripts or applications.
412
412
413 The only way to trigger plain mode is by setting either the
413 The only way to trigger plain mode is by setting either the
414 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
414 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
415
415
416 The return value can either be
416 The return value can either be
417 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
417 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
418 - True otherwise
418 - True otherwise
419 '''
419 '''
420 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
420 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
421 return False
421 return False
422 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
422 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
423 if feature and exceptions:
423 if feature and exceptions:
424 return feature not in exceptions
424 return feature not in exceptions
425 return True
425 return True
426
426
427 def username(self):
427 def username(self):
428 """Return default username to be used in commits.
428 """Return default username to be used in commits.
429
429
430 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
430 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
431 and stop searching if one of these is set.
431 and stop searching if one of these is set.
432 If not found and ui.askusername is True, ask the user, else use
432 If not found and ui.askusername is True, ask the user, else use
433 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
433 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
434 """
434 """
435 user = os.environ.get("HGUSER")
435 user = os.environ.get("HGUSER")
436 if user is None:
436 if user is None:
437 user = self.config("ui", "username")
437 user = self.config("ui", "username")
438 if user is not None:
438 if user is not None:
439 user = os.path.expandvars(user)
439 user = os.path.expandvars(user)
440 if user is None:
440 if user is None:
441 user = os.environ.get("EMAIL")
441 user = os.environ.get("EMAIL")
442 if user is None and self.configbool("ui", "askusername"):
442 if user is None and self.configbool("ui", "askusername"):
443 user = self.prompt(_("enter a commit username:"), default=None)
443 user = self.prompt(_("enter a commit username:"), default=None)
444 if user is None and not self.interactive():
444 if user is None and not self.interactive():
445 try:
445 try:
446 user = '%s@%s' % (util.getuser(), socket.getfqdn())
446 user = '%s@%s' % (util.getuser(), socket.getfqdn())
447 self.warn(_("no username found, using '%s' instead\n") % user)
447 self.warn(_("no username found, using '%s' instead\n") % user)
448 except KeyError:
448 except KeyError:
449 pass
449 pass
450 if not user:
450 if not user:
451 raise util.Abort(_('no username supplied (see "hg help config")'))
451 raise util.Abort(_('no username supplied (see "hg help config")'))
452 if "\n" in user:
452 if "\n" in user:
453 raise util.Abort(_("username %s contains a newline\n") % repr(user))
453 raise util.Abort(_("username %s contains a newline\n") % repr(user))
454 return user
454 return user
455
455
456 def shortuser(self, user):
456 def shortuser(self, user):
457 """Return a short representation of a user name or email address."""
457 """Return a short representation of a user name or email address."""
458 if not self.verbose:
458 if not self.verbose:
459 user = util.shortuser(user)
459 user = util.shortuser(user)
460 return user
460 return user
461
461
462 def expandpath(self, loc, default=None):
462 def expandpath(self, loc, default=None):
463 """Return repository location relative to cwd or from [paths]"""
463 """Return repository location relative to cwd or from [paths]"""
464 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
464 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
465 return loc
465 return loc
466
466
467 path = self.config('paths', loc)
467 path = self.config('paths', loc)
468 if not path and default is not None:
468 if not path and default is not None:
469 path = self.config('paths', default)
469 path = self.config('paths', default)
470 return path or loc
470 return path or loc
471
471
472 def pushbuffer(self):
472 def pushbuffer(self):
473 self._buffers.append([])
473 self._buffers.append([])
474
474
475 def popbuffer(self, labeled=False):
475 def popbuffer(self, labeled=False):
476 '''pop the last buffer and return the buffered output
476 '''pop the last buffer and return the buffered output
477
477
478 If labeled is True, any labels associated with buffered
478 If labeled is True, any labels associated with buffered
479 output will be handled. By default, this has no effect
479 output will be handled. By default, this has no effect
480 on the output returned, but extensions and GUI tools may
480 on the output returned, but extensions and GUI tools may
481 handle this argument and returned styled output. If output
481 handle this argument and returned styled output. If output
482 is being buffered so it can be captured and parsed or
482 is being buffered so it can be captured and parsed or
483 processed, labeled should not be set to True.
483 processed, labeled should not be set to True.
484 '''
484 '''
485 return "".join(self._buffers.pop())
485 return "".join(self._buffers.pop())
486
486
487 def write(self, *args, **opts):
487 def write(self, *args, **opts):
488 '''write args to output
488 '''write args to output
489
489
490 By default, this method simply writes to the buffer or stdout,
490 By default, this method simply writes to the buffer or stdout,
491 but extensions or GUI tools may override this method,
491 but extensions or GUI tools may override this method,
492 write_err(), popbuffer(), and label() to style output from
492 write_err(), popbuffer(), and label() to style output from
493 various parts of hg.
493 various parts of hg.
494
494
495 An optional keyword argument, "label", can be passed in.
495 An optional keyword argument, "label", can be passed in.
496 This should be a string containing label names separated by
496 This should be a string containing label names separated by
497 space. Label names take the form of "topic.type". For example,
497 space. Label names take the form of "topic.type". For example,
498 ui.debug() issues a label of "ui.debug".
498 ui.debug() issues a label of "ui.debug".
499
499
500 When labeling output for a specific command, a label of
500 When labeling output for a specific command, a label of
501 "cmdname.type" is recommended. For example, status issues
501 "cmdname.type" is recommended. For example, status issues
502 a label of "status.modified" for modified files.
502 a label of "status.modified" for modified files.
503 '''
503 '''
504 if self._buffers:
504 if self._buffers:
505 self._buffers[-1].extend([str(a) for a in args])
505 self._buffers[-1].extend([str(a) for a in args])
506 else:
506 else:
507 for a in args:
507 for a in args:
508 self.fout.write(str(a))
508 self.fout.write(str(a))
509
509
510 def write_err(self, *args, **opts):
510 def write_err(self, *args, **opts):
511 try:
511 try:
512 if not getattr(self.fout, 'closed', False):
512 if not getattr(self.fout, 'closed', False):
513 self.fout.flush()
513 self.fout.flush()
514 for a in args:
514 for a in args:
515 self.ferr.write(str(a))
515 self.ferr.write(str(a))
516 # stderr may be buffered under win32 when redirected to files,
516 # stderr may be buffered under win32 when redirected to files,
517 # including stdout.
517 # including stdout.
518 if not getattr(self.ferr, 'closed', False):
518 if not getattr(self.ferr, 'closed', False):
519 self.ferr.flush()
519 self.ferr.flush()
520 except IOError, inst:
520 except IOError, inst:
521 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
521 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
522 raise
522 raise
523
523
524 def flush(self):
524 def flush(self):
525 try: self.fout.flush()
525 try: self.fout.flush()
526 except (IOError, ValueError): pass
526 except (IOError, ValueError): pass
527 try: self.ferr.flush()
527 try: self.ferr.flush()
528 except (IOError, ValueError): pass
528 except (IOError, ValueError): pass
529
529
530 def _isatty(self, fh):
530 def _isatty(self, fh):
531 if self.configbool('ui', 'nontty', False):
531 if self.configbool('ui', 'nontty', False):
532 return False
532 return False
533 return util.isatty(fh)
533 return util.isatty(fh)
534
534
535 def interactive(self):
535 def interactive(self):
536 '''is interactive input allowed?
536 '''is interactive input allowed?
537
537
538 An interactive session is a session where input can be reasonably read
538 An interactive session is a session where input can be reasonably read
539 from `sys.stdin'. If this function returns false, any attempt to read
539 from `sys.stdin'. If this function returns false, any attempt to read
540 from stdin should fail with an error, unless a sensible default has been
540 from stdin should fail with an error, unless a sensible default has been
541 specified.
541 specified.
542
542
543 Interactiveness is triggered by the value of the `ui.interactive'
543 Interactiveness is triggered by the value of the `ui.interactive'
544 configuration variable or - if it is unset - when `sys.stdin' points
544 configuration variable or - if it is unset - when `sys.stdin' points
545 to a terminal device.
545 to a terminal device.
546
546
547 This function refers to input only; for output, see `ui.formatted()'.
547 This function refers to input only; for output, see `ui.formatted()'.
548 '''
548 '''
549 i = self.configbool("ui", "interactive", None)
549 i = self.configbool("ui", "interactive", None)
550 if i is None:
550 if i is None:
551 # some environments replace stdin without implementing isatty
551 # some environments replace stdin without implementing isatty
552 # usually those are non-interactive
552 # usually those are non-interactive
553 return self._isatty(self.fin)
553 return self._isatty(self.fin)
554
554
555 return i
555 return i
556
556
557 def termwidth(self):
557 def termwidth(self):
558 '''how wide is the terminal in columns?
558 '''how wide is the terminal in columns?
559 '''
559 '''
560 if 'COLUMNS' in os.environ:
560 if 'COLUMNS' in os.environ:
561 try:
561 try:
562 return int(os.environ['COLUMNS'])
562 return int(os.environ['COLUMNS'])
563 except ValueError:
563 except ValueError:
564 pass
564 pass
565 return util.termwidth()
565 return util.termwidth()
566
566
567 def formatted(self):
567 def formatted(self):
568 '''should formatted output be used?
568 '''should formatted output be used?
569
569
570 It is often desirable to format the output to suite the output medium.
570 It is often desirable to format the output to suite the output medium.
571 Examples of this are truncating long lines or colorizing messages.
571 Examples of this are truncating long lines or colorizing messages.
572 However, this is not often not desirable when piping output into other
572 However, this is not often not desirable when piping output into other
573 utilities, e.g. `grep'.
573 utilities, e.g. `grep'.
574
574
575 Formatted output is triggered by the value of the `ui.formatted'
575 Formatted output is triggered by the value of the `ui.formatted'
576 configuration variable or - if it is unset - when `sys.stdout' points
576 configuration variable or - if it is unset - when `sys.stdout' points
577 to a terminal device. Please note that `ui.formatted' should be
577 to a terminal device. Please note that `ui.formatted' should be
578 considered an implementation detail; it is not intended for use outside
578 considered an implementation detail; it is not intended for use outside
579 Mercurial or its extensions.
579 Mercurial or its extensions.
580
580
581 This function refers to output only; for input, see `ui.interactive()'.
581 This function refers to output only; for input, see `ui.interactive()'.
582 This function always returns false when in plain mode, see `ui.plain()'.
582 This function always returns false when in plain mode, see `ui.plain()'.
583 '''
583 '''
584 if self.plain():
584 if self.plain():
585 return False
585 return False
586
586
587 i = self.configbool("ui", "formatted", None)
587 i = self.configbool("ui", "formatted", None)
588 if i is None:
588 if i is None:
589 # some environments replace stdout without implementing isatty
589 # some environments replace stdout without implementing isatty
590 # usually those are non-interactive
590 # usually those are non-interactive
591 return self._isatty(self.fout)
591 return self._isatty(self.fout)
592
592
593 return i
593 return i
594
594
595 def _readline(self, prompt=''):
595 def _readline(self, prompt=''):
596 if self._isatty(self.fin):
596 if self._isatty(self.fin):
597 try:
597 try:
598 # magically add command line editing support, where
598 # magically add command line editing support, where
599 # available
599 # available
600 import readline
600 import readline
601 # force demandimport to really load the module
601 # force demandimport to really load the module
602 readline.read_history_file
602 readline.read_history_file
603 # windows sometimes raises something other than ImportError
603 # windows sometimes raises something other than ImportError
604 except Exception:
604 except Exception:
605 pass
605 pass
606
606
607 # call write() so output goes through subclassed implementation
607 # call write() so output goes through subclassed implementation
608 # e.g. color extension on Windows
608 # e.g. color extension on Windows
609 self.write(prompt)
609 self.write(prompt)
610
610
611 # instead of trying to emulate raw_input, swap (self.fin,
611 # instead of trying to emulate raw_input, swap (self.fin,
612 # self.fout) with (sys.stdin, sys.stdout)
612 # self.fout) with (sys.stdin, sys.stdout)
613 oldin = sys.stdin
613 oldin = sys.stdin
614 oldout = sys.stdout
614 oldout = sys.stdout
615 sys.stdin = self.fin
615 sys.stdin = self.fin
616 sys.stdout = self.fout
616 sys.stdout = self.fout
617 line = raw_input(' ')
617 line = raw_input(' ')
618 sys.stdin = oldin
618 sys.stdin = oldin
619 sys.stdout = oldout
619 sys.stdout = oldout
620
620
621 # When stdin is in binary mode on Windows, it can cause
621 # When stdin is in binary mode on Windows, it can cause
622 # raw_input() to emit an extra trailing carriage return
622 # raw_input() to emit an extra trailing carriage return
623 if os.linesep == '\r\n' and line and line[-1] == '\r':
623 if os.linesep == '\r\n' and line and line[-1] == '\r':
624 line = line[:-1]
624 line = line[:-1]
625 return line
625 return line
626
626
627 def prompt(self, msg, default="y"):
627 def prompt(self, msg, default="y"):
628 """Prompt user with msg, read response.
628 """Prompt user with msg, read response.
629 If ui is not interactive, the default is returned.
629 If ui is not interactive, the default is returned.
630 """
630 """
631 if not self.interactive():
631 if not self.interactive():
632 self.write(msg, ' ', default, "\n")
632 self.write(msg, ' ', default, "\n")
633 return default
633 return default
634 try:
634 try:
635 r = self._readline(self.label(msg, 'ui.prompt'))
635 r = self._readline(self.label(msg, 'ui.prompt'))
636 if not r:
636 if not r:
637 return default
637 return default
638 return r
638 return r
639 except EOFError:
639 except EOFError:
640 raise util.Abort(_('response expected'))
640 raise util.Abort(_('response expected'))
641
641
642 def promptchoice(self, prompt, default=0):
642 def promptchoice(self, prompt, default=0):
643 """Prompt user with a message, read response, and ensure it matches
643 """Prompt user with a message, read response, and ensure it matches
644 one of the provided choices. The prompt is formatted as follows:
644 one of the provided choices. The prompt is formatted as follows:
645
645
646 "would you like fries with that (Yn)? $$ &Yes $$ &No"
646 "would you like fries with that (Yn)? $$ &Yes $$ &No"
647
647
648 The index of the choice is returned. Responses are case
648 The index of the choice is returned. Responses are case
649 insensitive. If ui is not interactive, the default is
649 insensitive. If ui is not interactive, the default is
650 returned.
650 returned.
651 """
651 """
652
652
653 parts = prompt.split('$$')
653 parts = prompt.split('$$')
654 msg = parts[0].rstrip(' ')
654 msg = parts[0].rstrip(' ')
655 choices = [p.strip(' ') for p in parts[1:]]
655 choices = [p.strip(' ') for p in parts[1:]]
656 resps = [s[s.index('&') + 1].lower() for s in choices]
656 resps = [s[s.index('&') + 1].lower() for s in choices]
657 while True:
657 while True:
658 r = self.prompt(msg, resps[default])
658 r = self.prompt(msg, resps[default])
659 if r.lower() in resps:
659 if r.lower() in resps:
660 return resps.index(r.lower())
660 return resps.index(r.lower())
661 self.write(_("unrecognized response\n"))
661 self.write(_("unrecognized response\n"))
662
662
663 def getpass(self, prompt=None, default=None):
663 def getpass(self, prompt=None, default=None):
664 if not self.interactive():
664 if not self.interactive():
665 return default
665 return default
666 try:
666 try:
667 self.write(self.label(prompt or _('password: '), 'ui.prompt'))
667 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
668 return getpass.getpass('')
668 return getpass.getpass('')
669 except EOFError:
669 except EOFError:
670 raise util.Abort(_('response expected'))
670 raise util.Abort(_('response expected'))
671 def status(self, *msg, **opts):
671 def status(self, *msg, **opts):
672 '''write status message to output (if ui.quiet is False)
672 '''write status message to output (if ui.quiet is False)
673
673
674 This adds an output label of "ui.status".
674 This adds an output label of "ui.status".
675 '''
675 '''
676 if not self.quiet:
676 if not self.quiet:
677 opts['label'] = opts.get('label', '') + ' ui.status'
677 opts['label'] = opts.get('label', '') + ' ui.status'
678 self.write(*msg, **opts)
678 self.write(*msg, **opts)
679 def warn(self, *msg, **opts):
679 def warn(self, *msg, **opts):
680 '''write warning message to output (stderr)
680 '''write warning message to output (stderr)
681
681
682 This adds an output label of "ui.warning".
682 This adds an output label of "ui.warning".
683 '''
683 '''
684 opts['label'] = opts.get('label', '') + ' ui.warning'
684 opts['label'] = opts.get('label', '') + ' ui.warning'
685 self.write_err(*msg, **opts)
685 self.write_err(*msg, **opts)
686 def note(self, *msg, **opts):
686 def note(self, *msg, **opts):
687 '''write note to output (if ui.verbose is True)
687 '''write note to output (if ui.verbose is True)
688
688
689 This adds an output label of "ui.note".
689 This adds an output label of "ui.note".
690 '''
690 '''
691 if self.verbose:
691 if self.verbose:
692 opts['label'] = opts.get('label', '') + ' ui.note'
692 opts['label'] = opts.get('label', '') + ' ui.note'
693 self.write(*msg, **opts)
693 self.write(*msg, **opts)
694 def debug(self, *msg, **opts):
694 def debug(self, *msg, **opts):
695 '''write debug message to output (if ui.debugflag is True)
695 '''write debug message to output (if ui.debugflag is True)
696
696
697 This adds an output label of "ui.debug".
697 This adds an output label of "ui.debug".
698 '''
698 '''
699 if self.debugflag:
699 if self.debugflag:
700 opts['label'] = opts.get('label', '') + ' ui.debug'
700 opts['label'] = opts.get('label', '') + ' ui.debug'
701 self.write(*msg, **opts)
701 self.write(*msg, **opts)
702 def edit(self, text, user):
702 def edit(self, text, user):
703 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
703 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
704 text=True)
704 text=True)
705 try:
705 try:
706 f = os.fdopen(fd, "w")
706 f = os.fdopen(fd, "w")
707 f.write(text)
707 f.write(text)
708 f.close()
708 f.close()
709
709
710 editor = self.geteditor()
710 editor = self.geteditor()
711
711
712 util.system("%s \"%s\"" % (editor, name),
712 util.system("%s \"%s\"" % (editor, name),
713 environ={'HGUSER': user},
713 environ={'HGUSER': user},
714 onerr=util.Abort, errprefix=_("edit failed"),
714 onerr=util.Abort, errprefix=_("edit failed"),
715 out=self.fout)
715 out=self.fout)
716
716
717 f = open(name)
717 f = open(name)
718 t = f.read()
718 t = f.read()
719 f.close()
719 f.close()
720 finally:
720 finally:
721 os.unlink(name)
721 os.unlink(name)
722
722
723 return t
723 return t
724
724
725 def traceback(self, exc=None, force=False):
725 def traceback(self, exc=None, force=False):
726 '''print exception traceback if traceback printing enabled or forced.
726 '''print exception traceback if traceback printing enabled or forced.
727 only to call in exception handler. returns true if traceback
727 only to call in exception handler. returns true if traceback
728 printed.'''
728 printed.'''
729 if self.tracebackflag or force:
729 if self.tracebackflag or force:
730 if exc is None:
730 if exc is None:
731 exc = sys.exc_info()
731 exc = sys.exc_info()
732 cause = getattr(exc[1], 'cause', None)
732 cause = getattr(exc[1], 'cause', None)
733
733
734 if cause is not None:
734 if cause is not None:
735 causetb = traceback.format_tb(cause[2])
735 causetb = traceback.format_tb(cause[2])
736 exctb = traceback.format_tb(exc[2])
736 exctb = traceback.format_tb(exc[2])
737 exconly = traceback.format_exception_only(cause[0], cause[1])
737 exconly = traceback.format_exception_only(cause[0], cause[1])
738
738
739 # exclude frame where 'exc' was chained and rethrown from exctb
739 # exclude frame where 'exc' was chained and rethrown from exctb
740 self.write_err('Traceback (most recent call last):\n',
740 self.write_err('Traceback (most recent call last):\n',
741 ''.join(exctb[:-1]),
741 ''.join(exctb[:-1]),
742 ''.join(causetb),
742 ''.join(causetb),
743 ''.join(exconly))
743 ''.join(exconly))
744 else:
744 else:
745 traceback.print_exception(exc[0], exc[1], exc[2],
745 traceback.print_exception(exc[0], exc[1], exc[2],
746 file=self.ferr)
746 file=self.ferr)
747 return self.tracebackflag or force
747 return self.tracebackflag or force
748
748
749 def geteditor(self):
749 def geteditor(self):
750 '''return editor to use'''
750 '''return editor to use'''
751 if sys.platform == 'plan9':
751 if sys.platform == 'plan9':
752 # vi is the MIPS instruction simulator on Plan 9. We
752 # vi is the MIPS instruction simulator on Plan 9. We
753 # instead default to E to plumb commit messages to
753 # instead default to E to plumb commit messages to
754 # avoid confusion.
754 # avoid confusion.
755 editor = 'E'
755 editor = 'E'
756 else:
756 else:
757 editor = 'vi'
757 editor = 'vi'
758 return (os.environ.get("HGEDITOR") or
758 return (os.environ.get("HGEDITOR") or
759 self.config("ui", "editor") or
759 self.config("ui", "editor") or
760 os.environ.get("VISUAL") or
760 os.environ.get("VISUAL") or
761 os.environ.get("EDITOR", editor))
761 os.environ.get("EDITOR", editor))
762
762
763 def progress(self, topic, pos, item="", unit="", total=None):
763 def progress(self, topic, pos, item="", unit="", total=None):
764 '''show a progress message
764 '''show a progress message
765
765
766 With stock hg, this is simply a debug message that is hidden
766 With stock hg, this is simply a debug message that is hidden
767 by default, but with extensions or GUI tools it may be
767 by default, but with extensions or GUI tools it may be
768 visible. 'topic' is the current operation, 'item' is a
768 visible. 'topic' is the current operation, 'item' is a
769 non-numeric marker of the current position (i.e. the currently
769 non-numeric marker of the current position (i.e. the currently
770 in-process file), 'pos' is the current numeric position (i.e.
770 in-process file), 'pos' is the current numeric position (i.e.
771 revision, bytes, etc.), unit is a corresponding unit label,
771 revision, bytes, etc.), unit is a corresponding unit label,
772 and total is the highest expected pos.
772 and total is the highest expected pos.
773
773
774 Multiple nested topics may be active at a time.
774 Multiple nested topics may be active at a time.
775
775
776 All topics should be marked closed by setting pos to None at
776 All topics should be marked closed by setting pos to None at
777 termination.
777 termination.
778 '''
778 '''
779
779
780 if pos is None or not self.debugflag:
780 if pos is None or not self.debugflag:
781 return
781 return
782
782
783 if unit:
783 if unit:
784 unit = ' ' + unit
784 unit = ' ' + unit
785 if item:
785 if item:
786 item = ' ' + item
786 item = ' ' + item
787
787
788 if total:
788 if total:
789 pct = 100.0 * pos / total
789 pct = 100.0 * pos / total
790 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
790 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
791 % (topic, item, pos, total, unit, pct))
791 % (topic, item, pos, total, unit, pct))
792 else:
792 else:
793 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
793 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
794
794
795 def log(self, service, *msg, **opts):
795 def log(self, service, *msg, **opts):
796 '''hook for logging facility extensions
796 '''hook for logging facility extensions
797
797
798 service should be a readily-identifiable subsystem, which will
798 service should be a readily-identifiable subsystem, which will
799 allow filtering.
799 allow filtering.
800 message should be a newline-terminated string to log.
800 message should be a newline-terminated string to log.
801 '''
801 '''
802 pass
802 pass
803
803
804 def label(self, msg, label):
804 def label(self, msg, label):
805 '''style msg based on supplied label
805 '''style msg based on supplied label
806
806
807 Like ui.write(), this just returns msg unchanged, but extensions
807 Like ui.write(), this just returns msg unchanged, but extensions
808 and GUI tools can override it to allow styling output without
808 and GUI tools can override it to allow styling output without
809 writing it.
809 writing it.
810
810
811 ui.write(s, 'label') is equivalent to
811 ui.write(s, 'label') is equivalent to
812 ui.write(ui.label(s, 'label')).
812 ui.write(ui.label(s, 'label')).
813 '''
813 '''
814 return msg
814 return msg
General Comments 0
You need to be logged in to leave comments. Login now