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