##// END OF EJS Templates
os.isatty isn't available on Windows, so use sys.stdin.isatty()
Thomas Arendsen Hein -
r5039:c87de77a default
parent child Browse files
Show More
@@ -1,459 +1,459 b''
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 ui._isatty = os.isatty(sys.stdin.fileno())
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 self.interactive = self.configbool("ui", "interactive", None)
215 215 if self.interactive is None:
216 216 self.interactive = self.isatty()
217 217 if name is None or name == 'report_untrusted':
218 218 self.report_untrusted = (
219 219 self.configbool("ui", "report_untrusted", True))
220 220
221 221 # update trust information
222 222 if (section is None or section == 'trusted') and self.trusted_users:
223 223 for user in self.configlist('trusted', 'users'):
224 224 self.trusted_users[user] = 1
225 225 for group in self.configlist('trusted', 'groups'):
226 226 self.trusted_groups[group] = 1
227 227
228 228 def setconfig(self, section, name, value):
229 229 if not self.overlay:
230 230 self.overlay = util.configparser()
231 231 for cdata in (self.overlay, self.cdata, self.ucdata):
232 232 if not cdata: continue
233 233 if not cdata.has_section(section):
234 234 cdata.add_section(section)
235 235 cdata.set(section, name, value)
236 236 self.fixconfig(section, name, value)
237 237
238 238 def _get_cdata(self, untrusted):
239 239 if untrusted and self.ucdata:
240 240 return self.ucdata
241 241 return self.cdata
242 242
243 243 def _config(self, section, name, default, funcname, untrusted, abort):
244 244 cdata = self._get_cdata(untrusted)
245 245 if cdata.has_option(section, name):
246 246 try:
247 247 func = getattr(cdata, funcname)
248 248 return func(section, name)
249 249 except (ConfigParser.InterpolationError, ValueError), inst:
250 250 msg = _("Error in configuration section [%s] "
251 251 "parameter '%s':\n%s") % (section, name, inst)
252 252 if abort:
253 253 raise util.Abort(msg)
254 254 self.warn(_("Ignored: %s\n") % msg)
255 255 return default
256 256
257 257 def _configcommon(self, section, name, default, funcname, untrusted):
258 258 value = self._config(section, name, default, funcname,
259 259 untrusted, abort=True)
260 260 if self.debugflag and not untrusted and self.ucdata:
261 261 uvalue = self._config(section, name, None, funcname,
262 262 untrusted=True, abort=False)
263 263 if uvalue is not None and uvalue != value:
264 264 self.warn(_("Ignoring untrusted configuration option "
265 265 "%s.%s = %s\n") % (section, name, uvalue))
266 266 return value
267 267
268 268 def config(self, section, name, default=None, untrusted=False):
269 269 return self._configcommon(section, name, default, 'get', untrusted)
270 270
271 271 def configbool(self, section, name, default=False, untrusted=False):
272 272 return self._configcommon(section, name, default, 'getboolean',
273 273 untrusted)
274 274
275 275 def configlist(self, section, name, default=None, untrusted=False):
276 276 """Return a list of comma/space separated strings"""
277 277 result = self.config(section, name, untrusted=untrusted)
278 278 if result is None:
279 279 result = default or []
280 280 if isinstance(result, basestring):
281 281 result = result.replace(",", " ").split()
282 282 return result
283 283
284 284 def has_section(self, section, untrusted=False):
285 285 '''tell whether section exists in config.'''
286 286 cdata = self._get_cdata(untrusted)
287 287 return cdata.has_section(section)
288 288
289 289 def _configitems(self, section, untrusted, abort):
290 290 items = {}
291 291 cdata = self._get_cdata(untrusted)
292 292 if cdata.has_section(section):
293 293 try:
294 294 items.update(dict(cdata.items(section)))
295 295 except ConfigParser.InterpolationError, inst:
296 296 msg = _("Error in configuration section [%s]:\n"
297 297 "%s") % (section, inst)
298 298 if abort:
299 299 raise util.Abort(msg)
300 300 self.warn(_("Ignored: %s\n") % msg)
301 301 return items
302 302
303 303 def configitems(self, section, untrusted=False):
304 304 items = self._configitems(section, untrusted=untrusted, abort=True)
305 305 if self.debugflag and not untrusted and self.ucdata:
306 306 uitems = self._configitems(section, untrusted=True, abort=False)
307 307 keys = uitems.keys()
308 308 keys.sort()
309 309 for k in keys:
310 310 if uitems[k] != items.get(k):
311 311 self.warn(_("Ignoring untrusted configuration option "
312 312 "%s.%s = %s\n") % (section, k, uitems[k]))
313 313 x = items.items()
314 314 x.sort()
315 315 return x
316 316
317 317 def walkconfig(self, untrusted=False):
318 318 cdata = self._get_cdata(untrusted)
319 319 sections = cdata.sections()
320 320 sections.sort()
321 321 for section in sections:
322 322 for name, value in self.configitems(section, untrusted):
323 323 yield section, name, str(value).replace('\n', '\\n')
324 324
325 325 def username(self):
326 326 """Return default username to be used in commits.
327 327
328 328 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
329 329 and stop searching if one of these is set.
330 330 If not found, use ($LOGNAME or $USER or $LNAME or
331 331 $USERNAME) +"@full.hostname".
332 332 """
333 333 user = os.environ.get("HGUSER")
334 334 if user is None:
335 335 user = self.config("ui", "username")
336 336 if user is None:
337 337 user = os.environ.get("EMAIL")
338 338 if user is None:
339 339 try:
340 340 user = '%s@%s' % (util.getuser(), socket.getfqdn())
341 341 self.warn(_("No username found, using '%s' instead\n") % user)
342 342 except KeyError:
343 343 pass
344 344 if not user:
345 345 raise util.Abort(_("Please specify a username."))
346 346 return user
347 347
348 348 def shortuser(self, user):
349 349 """Return a short representation of a user name or email address."""
350 350 if not self.verbose: user = util.shortuser(user)
351 351 return user
352 352
353 353 def expandpath(self, loc, default=None):
354 354 """Return repository location relative to cwd or from [paths]"""
355 355 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
356 356 return loc
357 357
358 358 path = self.config("paths", loc)
359 359 if not path and default is not None:
360 360 path = self.config("paths", default)
361 361 return path or loc
362 362
363 363 def pushbuffer(self):
364 364 self.buffers.append([])
365 365
366 366 def popbuffer(self):
367 367 return "".join(self.buffers.pop())
368 368
369 369 def write(self, *args):
370 370 if self.buffers:
371 371 self.buffers[-1].extend([str(a) for a in args])
372 372 else:
373 373 for a in args:
374 374 sys.stdout.write(str(a))
375 375
376 376 def write_err(self, *args):
377 377 try:
378 378 if not sys.stdout.closed: sys.stdout.flush()
379 379 for a in args:
380 380 sys.stderr.write(str(a))
381 381 # stderr may be buffered under win32 when redirected to files,
382 382 # including stdout.
383 383 if not sys.stderr.closed: sys.stderr.flush()
384 384 except IOError, inst:
385 385 if inst.errno != errno.EPIPE:
386 386 raise
387 387
388 388 def flush(self):
389 389 try: sys.stdout.flush()
390 390 except: pass
391 391 try: sys.stderr.flush()
392 392 except: pass
393 393
394 394 def readline(self, prompt=''):
395 395 if self.isatty():
396 396 try:
397 397 # magically add command line editing support, where
398 398 # available
399 399 import readline
400 400 # force demandimport to really load the module
401 401 readline.read_history_file
402 402 except ImportError:
403 403 pass
404 404 return raw_input(prompt)
405 405
406 406 def prompt(self, msg, pat=None, default="y"):
407 407 if not self.interactive: return default
408 408 try:
409 409 r = self.readline(msg + ' ')
410 410 if not pat or re.match(pat, r):
411 411 return r
412 412 else:
413 413 self.write(_("unrecognized response\n"))
414 414 except EOFError:
415 415 raise util.Abort(_('response expected'))
416 416
417 417 def getpass(self, prompt=None, default=None):
418 418 if not self.interactive: return default
419 419 return getpass.getpass(prompt or _('password: '))
420 420 def status(self, *msg):
421 421 if not self.quiet: self.write(*msg)
422 422 def warn(self, *msg):
423 423 self.write_err(*msg)
424 424 def note(self, *msg):
425 425 if self.verbose: self.write(*msg)
426 426 def debug(self, *msg):
427 427 if self.debugflag: self.write(*msg)
428 428 def edit(self, text, user):
429 429 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
430 430 text=True)
431 431 try:
432 432 f = os.fdopen(fd, "w")
433 433 f.write(text)
434 434 f.close()
435 435
436 436 editor = (os.environ.get("HGEDITOR") or
437 437 self.config("ui", "editor") or
438 438 os.environ.get("EDITOR", "vi"))
439 439
440 440 util.system("%s \"%s\"" % (editor, name),
441 441 environ={'HGUSER': user},
442 442 onerr=util.Abort, errprefix=_("edit failed"))
443 443
444 444 f = open(name)
445 445 t = f.read()
446 446 f.close()
447 447 t = re.sub("(?m)^HG:.*\n", "", t)
448 448 finally:
449 449 os.unlink(name)
450 450
451 451 return t
452 452
453 453 def print_exc(self):
454 454 '''print exception traceback if traceback printing enabled.
455 455 only to call in exception handler. returns true if traceback
456 456 printed.'''
457 457 if self.traceback:
458 458 traceback.print_exc()
459 459 return self.traceback
General Comments 0
You need to be logged in to leave comments. Login now