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