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