##// END OF EJS Templates
ui: allow default when prompting
Matt Mackall -
r5709:9dc26941 default
parent child Browse files
Show More
@@ -1,473 +1,475
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9 import errno, getpass, os, re, socket, sys, tempfile
10 10 import ConfigParser, traceback, util
11 11
12 12 def dupconfig(orig):
13 13 new = util.configparser(orig.defaults())
14 14 updateconfig(orig, new)
15 15 return new
16 16
17 17 def updateconfig(source, dest, sections=None):
18 18 if not sections:
19 19 sections = source.sections()
20 20 for section in sections:
21 21 if not dest.has_section(section):
22 22 dest.add_section(section)
23 23 for name, value in source.items(section, raw=True):
24 24 dest.set(section, name, value)
25 25
26 26 class ui(object):
27 27 _isatty = None
28 28
29 29 def __init__(self, verbose=False, debug=False, quiet=False,
30 30 interactive=True, traceback=False, report_untrusted=True,
31 31 parentui=None):
32 32 self.overlay = None
33 33 self.buffers = []
34 34 if parentui is None:
35 35 # this is the parent of all ui children
36 36 self.parentui = None
37 37 self.quiet = quiet
38 38 self.verbose = verbose
39 39 self.debugflag = debug
40 40 self.interactive = interactive
41 41 self.traceback = traceback
42 42 self.report_untrusted = report_untrusted
43 43 self.trusted_users = {}
44 44 self.trusted_groups = {}
45 45 # if ucdata is not None, its keys must be a superset of cdata's
46 46 self.cdata = util.configparser()
47 47 self.ucdata = None
48 48 # we always trust global config files
49 49 self.check_trusted = False
50 50 self.readconfig(util.rcpath())
51 51 self.check_trusted = True
52 52 self.updateopts(verbose, debug, quiet, interactive)
53 53 else:
54 54 # parentui may point to an ui object which is already a child
55 55 self.parentui = parentui.parentui or parentui
56 56 self.trusted_users = parentui.trusted_users.copy()
57 57 self.trusted_groups = parentui.trusted_groups.copy()
58 58 self.cdata = dupconfig(self.parentui.cdata)
59 59 if self.parentui.ucdata:
60 60 self.ucdata = dupconfig(self.parentui.ucdata)
61 61 if self.parentui.overlay:
62 62 self.overlay = dupconfig(self.parentui.overlay)
63 63
64 64 def __getattr__(self, key):
65 65 return getattr(self.parentui, key)
66 66
67 67 def isatty(self):
68 68 if ui._isatty is None:
69 69 ui._isatty = sys.stdin.isatty()
70 70 return ui._isatty
71 71
72 72 def updateopts(self, verbose=False, debug=False, quiet=False,
73 73 interactive=True, traceback=False, config=[]):
74 74 for section, name, value in config:
75 75 self.setconfig(section, name, value)
76 76
77 77 if quiet or verbose or debug:
78 78 self.setconfig('ui', 'quiet', str(bool(quiet)))
79 79 self.setconfig('ui', 'verbose', str(bool(verbose)))
80 80 self.setconfig('ui', 'debug', str(bool(debug)))
81 81
82 82 self.verbosity_constraints()
83 83
84 84 if not interactive:
85 85 self.setconfig('ui', 'interactive', 'False')
86 86 self.interactive = False
87 87
88 88 self.traceback = self.traceback or traceback
89 89
90 90 def verbosity_constraints(self):
91 91 self.quiet = self.configbool('ui', 'quiet')
92 92 self.verbose = self.configbool('ui', 'verbose')
93 93 self.debugflag = self.configbool('ui', 'debug')
94 94
95 95 if self.debugflag:
96 96 self.verbose = True
97 97 self.quiet = False
98 98 elif self.verbose and self.quiet:
99 99 self.quiet = self.verbose = False
100 100
101 101 def _is_trusted(self, fp, f, warn=True):
102 102 if not self.check_trusted:
103 103 return True
104 104 st = util.fstat(fp)
105 105 if util.isowner(fp, st):
106 106 return True
107 107 tusers = self.trusted_users
108 108 tgroups = self.trusted_groups
109 109 if not tusers:
110 110 user = util.username()
111 111 if user is not None:
112 112 self.trusted_users[user] = 1
113 113 self.fixconfig(section='trusted')
114 114 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
115 115 user = util.username(st.st_uid)
116 116 group = util.groupname(st.st_gid)
117 117 if user not in tusers and group not in tgroups:
118 118 if warn and self.report_untrusted:
119 119 self.warn(_('Not trusting file %s from untrusted '
120 120 'user %s, group %s\n') % (f, user, group))
121 121 return False
122 122 return True
123 123
124 124 def readconfig(self, fn, root=None):
125 125 if isinstance(fn, basestring):
126 126 fn = [fn]
127 127 for f in fn:
128 128 try:
129 129 fp = open(f)
130 130 except IOError:
131 131 continue
132 132 cdata = self.cdata
133 133 trusted = self._is_trusted(fp, f)
134 134 if not trusted:
135 135 if self.ucdata is None:
136 136 self.ucdata = dupconfig(self.cdata)
137 137 cdata = self.ucdata
138 138 elif self.ucdata is not None:
139 139 # use a separate configparser, so that we don't accidentally
140 140 # override ucdata settings later on.
141 141 cdata = util.configparser()
142 142
143 143 try:
144 144 cdata.readfp(fp, f)
145 145 except ConfigParser.ParsingError, inst:
146 146 msg = _("Failed to parse %s\n%s") % (f, inst)
147 147 if trusted:
148 148 raise util.Abort(msg)
149 149 self.warn(_("Ignored: %s\n") % msg)
150 150
151 151 if trusted:
152 152 if cdata != self.cdata:
153 153 updateconfig(cdata, self.cdata)
154 154 if self.ucdata is not None:
155 155 updateconfig(cdata, self.ucdata)
156 156 # override data from config files with data set with ui.setconfig
157 157 if self.overlay:
158 158 updateconfig(self.overlay, self.cdata)
159 159 if root is None:
160 160 root = os.path.expanduser('~')
161 161 self.fixconfig(root=root)
162 162
163 163 def readsections(self, filename, *sections):
164 164 """Read filename and add only the specified sections to the config data
165 165
166 166 The settings are added to the trusted config data.
167 167 """
168 168 if not sections:
169 169 return
170 170
171 171 cdata = util.configparser()
172 172 try:
173 173 try:
174 174 fp = open(filename)
175 175 except IOError, inst:
176 176 raise util.Abort(_("unable to open %s: %s") %
177 177 (filename, getattr(inst, "strerror", inst)))
178 178 try:
179 179 cdata.readfp(fp, filename)
180 180 finally:
181 181 fp.close()
182 182 except ConfigParser.ParsingError, inst:
183 183 raise util.Abort(_("failed to parse %s\n%s") % (filename, inst))
184 184
185 185 for section in sections:
186 186 if not cdata.has_section(section):
187 187 cdata.add_section(section)
188 188
189 189 updateconfig(cdata, self.cdata, sections)
190 190 if self.ucdata:
191 191 updateconfig(cdata, self.ucdata, sections)
192 192
193 193 def fixconfig(self, section=None, name=None, value=None, root=None):
194 194 # translate paths relative to root (or home) into absolute paths
195 195 if section is None or section == 'paths':
196 196 if root is None:
197 197 root = os.getcwd()
198 198 items = section and [(name, value)] or []
199 199 for cdata in self.cdata, self.ucdata, self.overlay:
200 200 if not cdata: continue
201 201 if not items and cdata.has_section('paths'):
202 202 pathsitems = cdata.items('paths')
203 203 else:
204 204 pathsitems = items
205 205 for n, path in pathsitems:
206 206 if path and "://" not in path and not os.path.isabs(path):
207 207 cdata.set("paths", n, os.path.join(root, path))
208 208
209 209 # update verbosity/interactive/report_untrusted settings
210 210 if section is None or section == 'ui':
211 211 if name is None or name in ('quiet', 'verbose', 'debug'):
212 212 self.verbosity_constraints()
213 213 if name is None or name == 'interactive':
214 214 interactive = self.configbool("ui", "interactive", None)
215 215 if interactive is None and self.interactive:
216 216 self.interactive = self.isatty()
217 217 else:
218 218 self.interactive = interactive
219 219 if name is None or name == 'report_untrusted':
220 220 self.report_untrusted = (
221 221 self.configbool("ui", "report_untrusted", True))
222 222
223 223 # update trust information
224 224 if (section is None or section == 'trusted') and self.trusted_users:
225 225 for user in self.configlist('trusted', 'users'):
226 226 self.trusted_users[user] = 1
227 227 for group in self.configlist('trusted', 'groups'):
228 228 self.trusted_groups[group] = 1
229 229
230 230 def setconfig(self, section, name, value):
231 231 if not self.overlay:
232 232 self.overlay = util.configparser()
233 233 for cdata in (self.overlay, self.cdata, self.ucdata):
234 234 if not cdata: continue
235 235 if not cdata.has_section(section):
236 236 cdata.add_section(section)
237 237 cdata.set(section, name, value)
238 238 self.fixconfig(section, name, value)
239 239
240 240 def _get_cdata(self, untrusted):
241 241 if untrusted and self.ucdata:
242 242 return self.ucdata
243 243 return self.cdata
244 244
245 245 def _config(self, section, name, default, funcname, untrusted, abort):
246 246 cdata = self._get_cdata(untrusted)
247 247 if cdata.has_option(section, name):
248 248 try:
249 249 func = getattr(cdata, funcname)
250 250 return func(section, name)
251 251 except (ConfigParser.InterpolationError, ValueError), inst:
252 252 msg = _("Error in configuration section [%s] "
253 253 "parameter '%s':\n%s") % (section, name, inst)
254 254 if abort:
255 255 raise util.Abort(msg)
256 256 self.warn(_("Ignored: %s\n") % msg)
257 257 return default
258 258
259 259 def _configcommon(self, section, name, default, funcname, untrusted):
260 260 value = self._config(section, name, default, funcname,
261 261 untrusted, abort=True)
262 262 if self.debugflag and not untrusted and self.ucdata:
263 263 uvalue = self._config(section, name, None, funcname,
264 264 untrusted=True, abort=False)
265 265 if uvalue is not None and uvalue != value:
266 266 self.warn(_("Ignoring untrusted configuration option "
267 267 "%s.%s = %s\n") % (section, name, uvalue))
268 268 return value
269 269
270 270 def config(self, section, name, default=None, untrusted=False):
271 271 return self._configcommon(section, name, default, 'get', untrusted)
272 272
273 273 def configbool(self, section, name, default=False, untrusted=False):
274 274 return self._configcommon(section, name, default, 'getboolean',
275 275 untrusted)
276 276
277 277 def configlist(self, section, name, default=None, untrusted=False):
278 278 """Return a list of comma/space separated strings"""
279 279 result = self.config(section, name, untrusted=untrusted)
280 280 if result is None:
281 281 result = default or []
282 282 if isinstance(result, basestring):
283 283 result = result.replace(",", " ").split()
284 284 return result
285 285
286 286 def has_section(self, section, untrusted=False):
287 287 '''tell whether section exists in config.'''
288 288 cdata = self._get_cdata(untrusted)
289 289 return cdata.has_section(section)
290 290
291 291 def _configitems(self, section, untrusted, abort):
292 292 items = {}
293 293 cdata = self._get_cdata(untrusted)
294 294 if cdata.has_section(section):
295 295 try:
296 296 items.update(dict(cdata.items(section)))
297 297 except ConfigParser.InterpolationError, inst:
298 298 msg = _("Error in configuration section [%s]:\n"
299 299 "%s") % (section, inst)
300 300 if abort:
301 301 raise util.Abort(msg)
302 302 self.warn(_("Ignored: %s\n") % msg)
303 303 return items
304 304
305 305 def configitems(self, section, untrusted=False):
306 306 items = self._configitems(section, untrusted=untrusted, abort=True)
307 307 if self.debugflag and not untrusted and self.ucdata:
308 308 uitems = self._configitems(section, untrusted=True, abort=False)
309 309 keys = uitems.keys()
310 310 keys.sort()
311 311 for k in keys:
312 312 if uitems[k] != items.get(k):
313 313 self.warn(_("Ignoring untrusted configuration option "
314 314 "%s.%s = %s\n") % (section, k, uitems[k]))
315 315 x = items.items()
316 316 x.sort()
317 317 return x
318 318
319 319 def walkconfig(self, untrusted=False):
320 320 cdata = self._get_cdata(untrusted)
321 321 sections = cdata.sections()
322 322 sections.sort()
323 323 for section in sections:
324 324 for name, value in self.configitems(section, untrusted):
325 325 yield section, name, str(value).replace('\n', '\\n')
326 326
327 327 def username(self):
328 328 """Return default username to be used in commits.
329 329
330 330 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
331 331 and stop searching if one of these is set.
332 332 If not found, use ($LOGNAME or $USER or $LNAME or
333 333 $USERNAME) +"@full.hostname".
334 334 """
335 335 user = os.environ.get("HGUSER")
336 336 if user is None:
337 337 user = self.config("ui", "username")
338 338 if user is None:
339 339 user = os.environ.get("EMAIL")
340 340 if user is None:
341 341 try:
342 342 user = '%s@%s' % (util.getuser(), socket.getfqdn())
343 343 self.warn(_("No username found, using '%s' instead\n") % user)
344 344 except KeyError:
345 345 pass
346 346 if not user:
347 347 raise util.Abort(_("Please specify a username."))
348 348 return user
349 349
350 350 def shortuser(self, user):
351 351 """Return a short representation of a user name or email address."""
352 352 if not self.verbose: user = util.shortuser(user)
353 353 return user
354 354
355 355 def expandpath(self, loc, default=None):
356 356 """Return repository location relative to cwd or from [paths]"""
357 357 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
358 358 return loc
359 359
360 360 path = self.config("paths", loc)
361 361 if not path and default is not None:
362 362 path = self.config("paths", default)
363 363 return path or loc
364 364
365 365 def pushbuffer(self):
366 366 self.buffers.append([])
367 367
368 368 def popbuffer(self):
369 369 return "".join(self.buffers.pop())
370 370
371 371 def write(self, *args):
372 372 if self.buffers:
373 373 self.buffers[-1].extend([str(a) for a in args])
374 374 else:
375 375 for a in args:
376 376 sys.stdout.write(str(a))
377 377
378 378 def write_err(self, *args):
379 379 try:
380 380 if not sys.stdout.closed: sys.stdout.flush()
381 381 for a in args:
382 382 sys.stderr.write(str(a))
383 383 # stderr may be buffered under win32 when redirected to files,
384 384 # including stdout.
385 385 if not sys.stderr.closed: sys.stderr.flush()
386 386 except IOError, inst:
387 387 if inst.errno != errno.EPIPE:
388 388 raise
389 389
390 390 def flush(self):
391 391 try: sys.stdout.flush()
392 392 except: pass
393 393 try: sys.stderr.flush()
394 394 except: pass
395 395
396 396 def _readline(self, prompt=''):
397 397 if self.isatty():
398 398 try:
399 399 # magically add command line editing support, where
400 400 # available
401 401 import readline
402 402 # force demandimport to really load the module
403 403 readline.read_history_file
404 404 except ImportError:
405 405 pass
406 406 line = raw_input(prompt)
407 407 # When stdin is in binary mode on Windows, it can cause
408 408 # raw_input() to emit an extra trailing carriage return
409 409 if os.linesep == '\r\n' and line and line[-1] == '\r':
410 410 line = line[:-1]
411 411 return line
412 412
413 413 def prompt(self, msg, pat=None, default="y", matchflags=0):
414 414 if not self.interactive: return default
415 415 while True:
416 416 try:
417 417 r = self._readline(msg + ' ')
418 if not r:
419 return default
418 420 if not pat or re.match(pat, r, matchflags):
419 421 return r
420 422 else:
421 423 self.write(_("unrecognized response\n"))
422 424 except EOFError:
423 425 raise util.Abort(_('response expected'))
424 426
425 427 def getpass(self, prompt=None, default=None):
426 428 if not self.interactive: return default
427 429 return getpass.getpass(prompt or _('password: '))
428 430 def status(self, *msg):
429 431 if not self.quiet: self.write(*msg)
430 432 def warn(self, *msg):
431 433 self.write_err(*msg)
432 434 def note(self, *msg):
433 435 if self.verbose: self.write(*msg)
434 436 def debug(self, *msg):
435 437 if self.debugflag: self.write(*msg)
436 438 def edit(self, text, user):
437 439 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
438 440 text=True)
439 441 try:
440 442 f = os.fdopen(fd, "w")
441 443 f.write(text)
442 444 f.close()
443 445
444 446 editor = self.geteditor()
445 447
446 448 util.system("%s \"%s\"" % (editor, name),
447 449 environ={'HGUSER': user},
448 450 onerr=util.Abort, errprefix=_("edit failed"))
449 451
450 452 f = open(name)
451 453 t = f.read()
452 454 f.close()
453 455 t = re.sub("(?m)^HG:.*\n", "", t)
454 456 finally:
455 457 os.unlink(name)
456 458
457 459 return t
458 460
459 461 def print_exc(self):
460 462 '''print exception traceback if traceback printing enabled.
461 463 only to call in exception handler. returns true if traceback
462 464 printed.'''
463 465 if self.traceback:
464 466 traceback.print_exc()
465 467 return self.traceback
466 468
467 469 def geteditor(self):
468 470 '''return editor to use'''
469 471 return (os.environ.get("HGEDITOR") or
470 472 self.config("ui", "editor") or
471 473 os.environ.get("VISUAL") or
472 474 os.environ.get("EDITOR", "vi"))
473 475
General Comments 0
You need to be logged in to leave comments. Login now