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