##// END OF EJS Templates
i18n: show the non-ASCII password prompt text correctly...
FUJIWARA Katsunori -
r19085:be207d9b stable
parent child Browse files
Show More
@@ -1,812 +1,813 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, re, socket, sys, tempfile, traceback
9 import errno, getpass, os, re, 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 orig = string = self.config(section, name)
287 orig = string = self.config(section, name)
288 if orig is None:
288 if orig is None:
289 if not isinstance(default, str):
289 if not isinstance(default, str):
290 return default
290 return default
291 orig = string = default
291 orig = string = default
292 multiple = 1
292 multiple = 1
293 m = re.match(r'([^kmbg]+?)\s*([kmg]?)b?$', string, re.I)
293 m = re.match(r'([^kmbg]+?)\s*([kmg]?)b?$', string, re.I)
294 if m:
294 if m:
295 string, key = m.groups()
295 string, key = m.groups()
296 key = key.lower()
296 key = key.lower()
297 multiple = dict(k=1024, m=1048576, g=1073741824).get(key, 1)
297 multiple = dict(k=1024, m=1048576, g=1073741824).get(key, 1)
298 try:
298 try:
299 return int(float(string) * multiple)
299 return int(float(string) * multiple)
300 except ValueError:
300 except ValueError:
301 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
301 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
302 % (section, name, orig))
302 % (section, name, orig))
303
303
304 def configlist(self, section, name, default=None, untrusted=False):
304 def configlist(self, section, name, default=None, untrusted=False):
305 """parse a configuration element as a list of comma/space separated
305 """parse a configuration element as a list of comma/space separated
306 strings
306 strings
307
307
308 >>> u = ui(); s = 'foo'
308 >>> u = ui(); s = 'foo'
309 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
309 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
310 >>> u.configlist(s, 'list1')
310 >>> u.configlist(s, 'list1')
311 ['this', 'is', 'a small', 'test']
311 ['this', 'is', 'a small', 'test']
312 """
312 """
313
313
314 def _parse_plain(parts, s, offset):
314 def _parse_plain(parts, s, offset):
315 whitespace = False
315 whitespace = False
316 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
316 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
317 whitespace = True
317 whitespace = True
318 offset += 1
318 offset += 1
319 if offset >= len(s):
319 if offset >= len(s):
320 return None, parts, offset
320 return None, parts, offset
321 if whitespace:
321 if whitespace:
322 parts.append('')
322 parts.append('')
323 if s[offset] == '"' and not parts[-1]:
323 if s[offset] == '"' and not parts[-1]:
324 return _parse_quote, parts, offset + 1
324 return _parse_quote, parts, offset + 1
325 elif s[offset] == '"' and parts[-1][-1] == '\\':
325 elif s[offset] == '"' and parts[-1][-1] == '\\':
326 parts[-1] = parts[-1][:-1] + s[offset]
326 parts[-1] = parts[-1][:-1] + s[offset]
327 return _parse_plain, parts, offset + 1
327 return _parse_plain, parts, offset + 1
328 parts[-1] += s[offset]
328 parts[-1] += s[offset]
329 return _parse_plain, parts, offset + 1
329 return _parse_plain, parts, offset + 1
330
330
331 def _parse_quote(parts, s, offset):
331 def _parse_quote(parts, s, offset):
332 if offset < len(s) and s[offset] == '"': # ""
332 if offset < len(s) and s[offset] == '"': # ""
333 parts.append('')
333 parts.append('')
334 offset += 1
334 offset += 1
335 while offset < len(s) and (s[offset].isspace() or
335 while offset < len(s) and (s[offset].isspace() or
336 s[offset] == ','):
336 s[offset] == ','):
337 offset += 1
337 offset += 1
338 return _parse_plain, parts, offset
338 return _parse_plain, parts, offset
339
339
340 while offset < len(s) and s[offset] != '"':
340 while offset < len(s) and s[offset] != '"':
341 if (s[offset] == '\\' and offset + 1 < len(s)
341 if (s[offset] == '\\' and offset + 1 < len(s)
342 and s[offset + 1] == '"'):
342 and s[offset + 1] == '"'):
343 offset += 1
343 offset += 1
344 parts[-1] += '"'
344 parts[-1] += '"'
345 else:
345 else:
346 parts[-1] += s[offset]
346 parts[-1] += s[offset]
347 offset += 1
347 offset += 1
348
348
349 if offset >= len(s):
349 if offset >= len(s):
350 real_parts = _configlist(parts[-1])
350 real_parts = _configlist(parts[-1])
351 if not real_parts:
351 if not real_parts:
352 parts[-1] = '"'
352 parts[-1] = '"'
353 else:
353 else:
354 real_parts[0] = '"' + real_parts[0]
354 real_parts[0] = '"' + real_parts[0]
355 parts = parts[:-1]
355 parts = parts[:-1]
356 parts.extend(real_parts)
356 parts.extend(real_parts)
357 return None, parts, offset
357 return None, parts, offset
358
358
359 offset += 1
359 offset += 1
360 while offset < len(s) and s[offset] in [' ', ',']:
360 while offset < len(s) and s[offset] in [' ', ',']:
361 offset += 1
361 offset += 1
362
362
363 if offset < len(s):
363 if offset < len(s):
364 if offset + 1 == len(s) and s[offset] == '"':
364 if offset + 1 == len(s) and s[offset] == '"':
365 parts[-1] += '"'
365 parts[-1] += '"'
366 offset += 1
366 offset += 1
367 else:
367 else:
368 parts.append('')
368 parts.append('')
369 else:
369 else:
370 return None, parts, offset
370 return None, parts, offset
371
371
372 return _parse_plain, parts, offset
372 return _parse_plain, parts, offset
373
373
374 def _configlist(s):
374 def _configlist(s):
375 s = s.rstrip(' ,')
375 s = s.rstrip(' ,')
376 if not s:
376 if not s:
377 return []
377 return []
378 parser, parts, offset = _parse_plain, [''], 0
378 parser, parts, offset = _parse_plain, [''], 0
379 while parser:
379 while parser:
380 parser, parts, offset = parser(parts, s, offset)
380 parser, parts, offset = parser(parts, s, offset)
381 return parts
381 return parts
382
382
383 result = self.config(section, name, untrusted=untrusted)
383 result = self.config(section, name, untrusted=untrusted)
384 if result is None:
384 if result is None:
385 result = default or []
385 result = default or []
386 if isinstance(result, basestring):
386 if isinstance(result, basestring):
387 result = _configlist(result.lstrip(' ,\n'))
387 result = _configlist(result.lstrip(' ,\n'))
388 if result is None:
388 if result is None:
389 result = default or []
389 result = default or []
390 return result
390 return result
391
391
392 def has_section(self, section, untrusted=False):
392 def has_section(self, section, untrusted=False):
393 '''tell whether section exists in config.'''
393 '''tell whether section exists in config.'''
394 return section in self._data(untrusted)
394 return section in self._data(untrusted)
395
395
396 def configitems(self, section, untrusted=False):
396 def configitems(self, section, untrusted=False):
397 items = self._data(untrusted).items(section)
397 items = self._data(untrusted).items(section)
398 if self.debugflag and not untrusted and self._reportuntrusted:
398 if self.debugflag and not untrusted and self._reportuntrusted:
399 for k, v in self._ucfg.items(section):
399 for k, v in self._ucfg.items(section):
400 if self._tcfg.get(section, k) != v:
400 if self._tcfg.get(section, k) != v:
401 self.debug("ignoring untrusted configuration option "
401 self.debug("ignoring untrusted configuration option "
402 "%s.%s = %s\n" % (section, k, v))
402 "%s.%s = %s\n" % (section, k, v))
403 return items
403 return items
404
404
405 def walkconfig(self, untrusted=False):
405 def walkconfig(self, untrusted=False):
406 cfg = self._data(untrusted)
406 cfg = self._data(untrusted)
407 for section in cfg.sections():
407 for section in cfg.sections():
408 for name, value in self.configitems(section, untrusted):
408 for name, value in self.configitems(section, untrusted):
409 yield section, name, value
409 yield section, name, value
410
410
411 def plain(self, feature=None):
411 def plain(self, feature=None):
412 '''is plain mode active?
412 '''is plain mode active?
413
413
414 Plain mode means that all configuration variables which affect
414 Plain mode means that all configuration variables which affect
415 the behavior and output of Mercurial should be
415 the behavior and output of Mercurial should be
416 ignored. Additionally, the output should be stable,
416 ignored. Additionally, the output should be stable,
417 reproducible and suitable for use in scripts or applications.
417 reproducible and suitable for use in scripts or applications.
418
418
419 The only way to trigger plain mode is by setting either the
419 The only way to trigger plain mode is by setting either the
420 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
420 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
421
421
422 The return value can either be
422 The return value can either be
423 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
423 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
424 - True otherwise
424 - True otherwise
425 '''
425 '''
426 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
426 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
427 return False
427 return False
428 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
428 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
429 if feature and exceptions:
429 if feature and exceptions:
430 return feature not in exceptions
430 return feature not in exceptions
431 return True
431 return True
432
432
433 def username(self):
433 def username(self):
434 """Return default username to be used in commits.
434 """Return default username to be used in commits.
435
435
436 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
436 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
437 and stop searching if one of these is set.
437 and stop searching if one of these is set.
438 If not found and ui.askusername is True, ask the user, else use
438 If not found and ui.askusername is True, ask the user, else use
439 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
439 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
440 """
440 """
441 user = os.environ.get("HGUSER")
441 user = os.environ.get("HGUSER")
442 if user is None:
442 if user is None:
443 user = self.config("ui", "username")
443 user = self.config("ui", "username")
444 if user is not None:
444 if user is not None:
445 user = os.path.expandvars(user)
445 user = os.path.expandvars(user)
446 if user is None:
446 if user is None:
447 user = os.environ.get("EMAIL")
447 user = os.environ.get("EMAIL")
448 if user is None and self.configbool("ui", "askusername"):
448 if user is None and self.configbool("ui", "askusername"):
449 user = self.prompt(_("enter a commit username:"), default=None)
449 user = self.prompt(_("enter a commit username:"), default=None)
450 if user is None and not self.interactive():
450 if user is None and not self.interactive():
451 try:
451 try:
452 user = '%s@%s' % (util.getuser(), socket.getfqdn())
452 user = '%s@%s' % (util.getuser(), socket.getfqdn())
453 self.warn(_("no username found, using '%s' instead\n") % user)
453 self.warn(_("no username found, using '%s' instead\n") % user)
454 except KeyError:
454 except KeyError:
455 pass
455 pass
456 if not user:
456 if not user:
457 raise util.Abort(_('no username supplied (see "hg help config")'))
457 raise util.Abort(_('no username supplied (see "hg help config")'))
458 if "\n" in user:
458 if "\n" in user:
459 raise util.Abort(_("username %s contains a newline\n") % repr(user))
459 raise util.Abort(_("username %s contains a newline\n") % repr(user))
460 return user
460 return user
461
461
462 def shortuser(self, user):
462 def shortuser(self, user):
463 """Return a short representation of a user name or email address."""
463 """Return a short representation of a user name or email address."""
464 if not self.verbose:
464 if not self.verbose:
465 user = util.shortuser(user)
465 user = util.shortuser(user)
466 return user
466 return user
467
467
468 def expandpath(self, loc, default=None):
468 def expandpath(self, loc, default=None):
469 """Return repository location relative to cwd or from [paths]"""
469 """Return repository location relative to cwd or from [paths]"""
470 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
470 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
471 return loc
471 return loc
472
472
473 path = self.config('paths', loc)
473 path = self.config('paths', loc)
474 if not path and default is not None:
474 if not path and default is not None:
475 path = self.config('paths', default)
475 path = self.config('paths', default)
476 return path or loc
476 return path or loc
477
477
478 def pushbuffer(self):
478 def pushbuffer(self):
479 self._buffers.append([])
479 self._buffers.append([])
480
480
481 def popbuffer(self, labeled=False):
481 def popbuffer(self, labeled=False):
482 '''pop the last buffer and return the buffered output
482 '''pop the last buffer and return the buffered output
483
483
484 If labeled is True, any labels associated with buffered
484 If labeled is True, any labels associated with buffered
485 output will be handled. By default, this has no effect
485 output will be handled. By default, this has no effect
486 on the output returned, but extensions and GUI tools may
486 on the output returned, but extensions and GUI tools may
487 handle this argument and returned styled output. If output
487 handle this argument and returned styled output. If output
488 is being buffered so it can be captured and parsed or
488 is being buffered so it can be captured and parsed or
489 processed, labeled should not be set to True.
489 processed, labeled should not be set to True.
490 '''
490 '''
491 return "".join(self._buffers.pop())
491 return "".join(self._buffers.pop())
492
492
493 def write(self, *args, **opts):
493 def write(self, *args, **opts):
494 '''write args to output
494 '''write args to output
495
495
496 By default, this method simply writes to the buffer or stdout,
496 By default, this method simply writes to the buffer or stdout,
497 but extensions or GUI tools may override this method,
497 but extensions or GUI tools may override this method,
498 write_err(), popbuffer(), and label() to style output from
498 write_err(), popbuffer(), and label() to style output from
499 various parts of hg.
499 various parts of hg.
500
500
501 An optional keyword argument, "label", can be passed in.
501 An optional keyword argument, "label", can be passed in.
502 This should be a string containing label names separated by
502 This should be a string containing label names separated by
503 space. Label names take the form of "topic.type". For example,
503 space. Label names take the form of "topic.type". For example,
504 ui.debug() issues a label of "ui.debug".
504 ui.debug() issues a label of "ui.debug".
505
505
506 When labeling output for a specific command, a label of
506 When labeling output for a specific command, a label of
507 "cmdname.type" is recommended. For example, status issues
507 "cmdname.type" is recommended. For example, status issues
508 a label of "status.modified" for modified files.
508 a label of "status.modified" for modified files.
509 '''
509 '''
510 if self._buffers:
510 if self._buffers:
511 self._buffers[-1].extend([str(a) for a in args])
511 self._buffers[-1].extend([str(a) for a in args])
512 else:
512 else:
513 for a in args:
513 for a in args:
514 self.fout.write(str(a))
514 self.fout.write(str(a))
515
515
516 def write_err(self, *args, **opts):
516 def write_err(self, *args, **opts):
517 try:
517 try:
518 if not getattr(self.fout, 'closed', False):
518 if not getattr(self.fout, 'closed', False):
519 self.fout.flush()
519 self.fout.flush()
520 for a in args:
520 for a in args:
521 self.ferr.write(str(a))
521 self.ferr.write(str(a))
522 # stderr may be buffered under win32 when redirected to files,
522 # stderr may be buffered under win32 when redirected to files,
523 # including stdout.
523 # including stdout.
524 if not getattr(self.ferr, 'closed', False):
524 if not getattr(self.ferr, 'closed', False):
525 self.ferr.flush()
525 self.ferr.flush()
526 except IOError, inst:
526 except IOError, inst:
527 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
527 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
528 raise
528 raise
529
529
530 def flush(self):
530 def flush(self):
531 try: self.fout.flush()
531 try: self.fout.flush()
532 except (IOError, ValueError): pass
532 except (IOError, ValueError): pass
533 try: self.ferr.flush()
533 try: self.ferr.flush()
534 except (IOError, ValueError): pass
534 except (IOError, ValueError): pass
535
535
536 def _isatty(self, fh):
536 def _isatty(self, fh):
537 if self.configbool('ui', 'nontty', False):
537 if self.configbool('ui', 'nontty', False):
538 return False
538 return False
539 return util.isatty(fh)
539 return util.isatty(fh)
540
540
541 def interactive(self):
541 def interactive(self):
542 '''is interactive input allowed?
542 '''is interactive input allowed?
543
543
544 An interactive session is a session where input can be reasonably read
544 An interactive session is a session where input can be reasonably read
545 from `sys.stdin'. If this function returns false, any attempt to read
545 from `sys.stdin'. If this function returns false, any attempt to read
546 from stdin should fail with an error, unless a sensible default has been
546 from stdin should fail with an error, unless a sensible default has been
547 specified.
547 specified.
548
548
549 Interactiveness is triggered by the value of the `ui.interactive'
549 Interactiveness is triggered by the value of the `ui.interactive'
550 configuration variable or - if it is unset - when `sys.stdin' points
550 configuration variable or - if it is unset - when `sys.stdin' points
551 to a terminal device.
551 to a terminal device.
552
552
553 This function refers to input only; for output, see `ui.formatted()'.
553 This function refers to input only; for output, see `ui.formatted()'.
554 '''
554 '''
555 i = self.configbool("ui", "interactive", None)
555 i = self.configbool("ui", "interactive", None)
556 if i is None:
556 if i is None:
557 # some environments replace stdin without implementing isatty
557 # some environments replace stdin without implementing isatty
558 # usually those are non-interactive
558 # usually those are non-interactive
559 return self._isatty(self.fin)
559 return self._isatty(self.fin)
560
560
561 return i
561 return i
562
562
563 def termwidth(self):
563 def termwidth(self):
564 '''how wide is the terminal in columns?
564 '''how wide is the terminal in columns?
565 '''
565 '''
566 if 'COLUMNS' in os.environ:
566 if 'COLUMNS' in os.environ:
567 try:
567 try:
568 return int(os.environ['COLUMNS'])
568 return int(os.environ['COLUMNS'])
569 except ValueError:
569 except ValueError:
570 pass
570 pass
571 return util.termwidth()
571 return util.termwidth()
572
572
573 def formatted(self):
573 def formatted(self):
574 '''should formatted output be used?
574 '''should formatted output be used?
575
575
576 It is often desirable to format the output to suite the output medium.
576 It is often desirable to format the output to suite the output medium.
577 Examples of this are truncating long lines or colorizing messages.
577 Examples of this are truncating long lines or colorizing messages.
578 However, this is not often not desirable when piping output into other
578 However, this is not often not desirable when piping output into other
579 utilities, e.g. `grep'.
579 utilities, e.g. `grep'.
580
580
581 Formatted output is triggered by the value of the `ui.formatted'
581 Formatted output is triggered by the value of the `ui.formatted'
582 configuration variable or - if it is unset - when `sys.stdout' points
582 configuration variable or - if it is unset - when `sys.stdout' points
583 to a terminal device. Please note that `ui.formatted' should be
583 to a terminal device. Please note that `ui.formatted' should be
584 considered an implementation detail; it is not intended for use outside
584 considered an implementation detail; it is not intended for use outside
585 Mercurial or its extensions.
585 Mercurial or its extensions.
586
586
587 This function refers to output only; for input, see `ui.interactive()'.
587 This function refers to output only; for input, see `ui.interactive()'.
588 This function always returns false when in plain mode, see `ui.plain()'.
588 This function always returns false when in plain mode, see `ui.plain()'.
589 '''
589 '''
590 if self.plain():
590 if self.plain():
591 return False
591 return False
592
592
593 i = self.configbool("ui", "formatted", None)
593 i = self.configbool("ui", "formatted", None)
594 if i is None:
594 if i is None:
595 # some environments replace stdout without implementing isatty
595 # some environments replace stdout without implementing isatty
596 # usually those are non-interactive
596 # usually those are non-interactive
597 return self._isatty(self.fout)
597 return self._isatty(self.fout)
598
598
599 return i
599 return i
600
600
601 def _readline(self, prompt=''):
601 def _readline(self, prompt=''):
602 if self._isatty(self.fin):
602 if self._isatty(self.fin):
603 try:
603 try:
604 # magically add command line editing support, where
604 # magically add command line editing support, where
605 # available
605 # available
606 import readline
606 import readline
607 # force demandimport to really load the module
607 # force demandimport to really load the module
608 readline.read_history_file
608 readline.read_history_file
609 # windows sometimes raises something other than ImportError
609 # windows sometimes raises something other than ImportError
610 except Exception:
610 except Exception:
611 pass
611 pass
612
612
613 # call write() so output goes through subclassed implementation
613 # call write() so output goes through subclassed implementation
614 # e.g. color extension on Windows
614 # e.g. color extension on Windows
615 self.write(prompt)
615 self.write(prompt)
616
616
617 # instead of trying to emulate raw_input, swap (self.fin,
617 # instead of trying to emulate raw_input, swap (self.fin,
618 # self.fout) with (sys.stdin, sys.stdout)
618 # self.fout) with (sys.stdin, sys.stdout)
619 oldin = sys.stdin
619 oldin = sys.stdin
620 oldout = sys.stdout
620 oldout = sys.stdout
621 sys.stdin = self.fin
621 sys.stdin = self.fin
622 sys.stdout = self.fout
622 sys.stdout = self.fout
623 line = raw_input(' ')
623 line = raw_input(' ')
624 sys.stdin = oldin
624 sys.stdin = oldin
625 sys.stdout = oldout
625 sys.stdout = oldout
626
626
627 # When stdin is in binary mode on Windows, it can cause
627 # When stdin is in binary mode on Windows, it can cause
628 # raw_input() to emit an extra trailing carriage return
628 # raw_input() to emit an extra trailing carriage return
629 if os.linesep == '\r\n' and line and line[-1] == '\r':
629 if os.linesep == '\r\n' and line and line[-1] == '\r':
630 line = line[:-1]
630 line = line[:-1]
631 return line
631 return line
632
632
633 def prompt(self, msg, default="y"):
633 def prompt(self, msg, default="y"):
634 """Prompt user with msg, read response.
634 """Prompt user with msg, read response.
635 If ui is not interactive, the default is returned.
635 If ui is not interactive, the default is returned.
636 """
636 """
637 if not self.interactive():
637 if not self.interactive():
638 self.write(msg, ' ', default, "\n")
638 self.write(msg, ' ', default, "\n")
639 return default
639 return default
640 try:
640 try:
641 r = self._readline(self.label(msg, 'ui.prompt'))
641 r = self._readline(self.label(msg, 'ui.prompt'))
642 if not r:
642 if not r:
643 return default
643 return default
644 return r
644 return r
645 except EOFError:
645 except EOFError:
646 raise util.Abort(_('response expected'))
646 raise util.Abort(_('response expected'))
647
647
648 def promptchoice(self, msg, choices, default=0):
648 def promptchoice(self, msg, choices, default=0):
649 """Prompt user with msg, read response, and ensure it matches
649 """Prompt user with msg, read response, and ensure it matches
650 one of the provided choices. The index of the choice is returned.
650 one of the provided choices. The index of the choice is returned.
651 choices is a sequence of acceptable responses with the format:
651 choices is a sequence of acceptable responses with the format:
652 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
652 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
653 If ui is not interactive, the default is returned.
653 If ui is not interactive, the default is returned.
654 """
654 """
655 resps = [s[s.index('&') + 1].lower() for s in choices]
655 resps = [s[s.index('&') + 1].lower() for s in choices]
656 while True:
656 while True:
657 r = self.prompt(msg, resps[default])
657 r = self.prompt(msg, resps[default])
658 if r.lower() in resps:
658 if r.lower() in resps:
659 return resps.index(r.lower())
659 return resps.index(r.lower())
660 self.write(_("unrecognized response\n"))
660 self.write(_("unrecognized response\n"))
661
661
662 def getpass(self, prompt=None, default=None):
662 def getpass(self, prompt=None, default=None):
663 if not self.interactive():
663 if not self.interactive():
664 return default
664 return default
665 try:
665 try:
666 return getpass.getpass(prompt or _('password: '))
666 self.write(self.label(prompt or _('password: '), 'ui.prompt'))
667 return getpass.getpass('')
667 except EOFError:
668 except EOFError:
668 raise util.Abort(_('response expected'))
669 raise util.Abort(_('response expected'))
669 def status(self, *msg, **opts):
670 def status(self, *msg, **opts):
670 '''write status message to output (if ui.quiet is False)
671 '''write status message to output (if ui.quiet is False)
671
672
672 This adds an output label of "ui.status".
673 This adds an output label of "ui.status".
673 '''
674 '''
674 if not self.quiet:
675 if not self.quiet:
675 opts['label'] = opts.get('label', '') + ' ui.status'
676 opts['label'] = opts.get('label', '') + ' ui.status'
676 self.write(*msg, **opts)
677 self.write(*msg, **opts)
677 def warn(self, *msg, **opts):
678 def warn(self, *msg, **opts):
678 '''write warning message to output (stderr)
679 '''write warning message to output (stderr)
679
680
680 This adds an output label of "ui.warning".
681 This adds an output label of "ui.warning".
681 '''
682 '''
682 opts['label'] = opts.get('label', '') + ' ui.warning'
683 opts['label'] = opts.get('label', '') + ' ui.warning'
683 self.write_err(*msg, **opts)
684 self.write_err(*msg, **opts)
684 def note(self, *msg, **opts):
685 def note(self, *msg, **opts):
685 '''write note to output (if ui.verbose is True)
686 '''write note to output (if ui.verbose is True)
686
687
687 This adds an output label of "ui.note".
688 This adds an output label of "ui.note".
688 '''
689 '''
689 if self.verbose:
690 if self.verbose:
690 opts['label'] = opts.get('label', '') + ' ui.note'
691 opts['label'] = opts.get('label', '') + ' ui.note'
691 self.write(*msg, **opts)
692 self.write(*msg, **opts)
692 def debug(self, *msg, **opts):
693 def debug(self, *msg, **opts):
693 '''write debug message to output (if ui.debugflag is True)
694 '''write debug message to output (if ui.debugflag is True)
694
695
695 This adds an output label of "ui.debug".
696 This adds an output label of "ui.debug".
696 '''
697 '''
697 if self.debugflag:
698 if self.debugflag:
698 opts['label'] = opts.get('label', '') + ' ui.debug'
699 opts['label'] = opts.get('label', '') + ' ui.debug'
699 self.write(*msg, **opts)
700 self.write(*msg, **opts)
700 def edit(self, text, user):
701 def edit(self, text, user):
701 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
702 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
702 text=True)
703 text=True)
703 try:
704 try:
704 f = os.fdopen(fd, "w")
705 f = os.fdopen(fd, "w")
705 f.write(text)
706 f.write(text)
706 f.close()
707 f.close()
707
708
708 editor = self.geteditor()
709 editor = self.geteditor()
709
710
710 util.system("%s \"%s\"" % (editor, name),
711 util.system("%s \"%s\"" % (editor, name),
711 environ={'HGUSER': user},
712 environ={'HGUSER': user},
712 onerr=util.Abort, errprefix=_("edit failed"),
713 onerr=util.Abort, errprefix=_("edit failed"),
713 out=self.fout)
714 out=self.fout)
714
715
715 f = open(name)
716 f = open(name)
716 t = f.read()
717 t = f.read()
717 f.close()
718 f.close()
718 finally:
719 finally:
719 os.unlink(name)
720 os.unlink(name)
720
721
721 return t
722 return t
722
723
723 def traceback(self, exc=None, force=False):
724 def traceback(self, exc=None, force=False):
724 '''print exception traceback if traceback printing enabled or forced.
725 '''print exception traceback if traceback printing enabled or forced.
725 only to call in exception handler. returns true if traceback
726 only to call in exception handler. returns true if traceback
726 printed.'''
727 printed.'''
727 if self.tracebackflag or force:
728 if self.tracebackflag or force:
728 if exc is None:
729 if exc is None:
729 exc = sys.exc_info()
730 exc = sys.exc_info()
730 cause = getattr(exc[1], 'cause', None)
731 cause = getattr(exc[1], 'cause', None)
731
732
732 if cause is not None:
733 if cause is not None:
733 causetb = traceback.format_tb(cause[2])
734 causetb = traceback.format_tb(cause[2])
734 exctb = traceback.format_tb(exc[2])
735 exctb = traceback.format_tb(exc[2])
735 exconly = traceback.format_exception_only(cause[0], cause[1])
736 exconly = traceback.format_exception_only(cause[0], cause[1])
736
737
737 # exclude frame where 'exc' was chained and rethrown from exctb
738 # exclude frame where 'exc' was chained and rethrown from exctb
738 self.write_err('Traceback (most recent call last):\n',
739 self.write_err('Traceback (most recent call last):\n',
739 ''.join(exctb[:-1]),
740 ''.join(exctb[:-1]),
740 ''.join(causetb),
741 ''.join(causetb),
741 ''.join(exconly))
742 ''.join(exconly))
742 else:
743 else:
743 traceback.print_exception(exc[0], exc[1], exc[2],
744 traceback.print_exception(exc[0], exc[1], exc[2],
744 file=self.ferr)
745 file=self.ferr)
745 return self.tracebackflag or force
746 return self.tracebackflag or force
746
747
747 def geteditor(self):
748 def geteditor(self):
748 '''return editor to use'''
749 '''return editor to use'''
749 if sys.platform == 'plan9':
750 if sys.platform == 'plan9':
750 # vi is the MIPS instruction simulator on Plan 9. We
751 # vi is the MIPS instruction simulator on Plan 9. We
751 # instead default to E to plumb commit messages to
752 # instead default to E to plumb commit messages to
752 # avoid confusion.
753 # avoid confusion.
753 editor = 'E'
754 editor = 'E'
754 else:
755 else:
755 editor = 'vi'
756 editor = 'vi'
756 return (os.environ.get("HGEDITOR") or
757 return (os.environ.get("HGEDITOR") or
757 self.config("ui", "editor") or
758 self.config("ui", "editor") or
758 os.environ.get("VISUAL") or
759 os.environ.get("VISUAL") or
759 os.environ.get("EDITOR", editor))
760 os.environ.get("EDITOR", editor))
760
761
761 def progress(self, topic, pos, item="", unit="", total=None):
762 def progress(self, topic, pos, item="", unit="", total=None):
762 '''show a progress message
763 '''show a progress message
763
764
764 With stock hg, this is simply a debug message that is hidden
765 With stock hg, this is simply a debug message that is hidden
765 by default, but with extensions or GUI tools it may be
766 by default, but with extensions or GUI tools it may be
766 visible. 'topic' is the current operation, 'item' is a
767 visible. 'topic' is the current operation, 'item' is a
767 non-numeric marker of the current position (i.e. the currently
768 non-numeric marker of the current position (i.e. the currently
768 in-process file), 'pos' is the current numeric position (i.e.
769 in-process file), 'pos' is the current numeric position (i.e.
769 revision, bytes, etc.), unit is a corresponding unit label,
770 revision, bytes, etc.), unit is a corresponding unit label,
770 and total is the highest expected pos.
771 and total is the highest expected pos.
771
772
772 Multiple nested topics may be active at a time.
773 Multiple nested topics may be active at a time.
773
774
774 All topics should be marked closed by setting pos to None at
775 All topics should be marked closed by setting pos to None at
775 termination.
776 termination.
776 '''
777 '''
777
778
778 if pos is None or not self.debugflag:
779 if pos is None or not self.debugflag:
779 return
780 return
780
781
781 if unit:
782 if unit:
782 unit = ' ' + unit
783 unit = ' ' + unit
783 if item:
784 if item:
784 item = ' ' + item
785 item = ' ' + item
785
786
786 if total:
787 if total:
787 pct = 100.0 * pos / total
788 pct = 100.0 * pos / total
788 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
789 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
789 % (topic, item, pos, total, unit, pct))
790 % (topic, item, pos, total, unit, pct))
790 else:
791 else:
791 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
792 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
792
793
793 def log(self, service, *msg, **opts):
794 def log(self, service, *msg, **opts):
794 '''hook for logging facility extensions
795 '''hook for logging facility extensions
795
796
796 service should be a readily-identifiable subsystem, which will
797 service should be a readily-identifiable subsystem, which will
797 allow filtering.
798 allow filtering.
798 message should be a newline-terminated string to log.
799 message should be a newline-terminated string to log.
799 '''
800 '''
800 pass
801 pass
801
802
802 def label(self, msg, label):
803 def label(self, msg, label):
803 '''style msg based on supplied label
804 '''style msg based on supplied label
804
805
805 Like ui.write(), this just returns msg unchanged, but extensions
806 Like ui.write(), this just returns msg unchanged, but extensions
806 and GUI tools can override it to allow styling output without
807 and GUI tools can override it to allow styling output without
807 writing it.
808 writing it.
808
809
809 ui.write(s, 'label') is equivalent to
810 ui.write(s, 'label') is equivalent to
810 ui.write(ui.label(s, 'label')).
811 ui.write(ui.label(s, 'label')).
811 '''
812 '''
812 return msg
813 return msg
General Comments 0
You need to be logged in to leave comments. Login now