##// END OF EJS Templates
ui: ui.copy() now takes the ui class into account...
Ronny Pfannschmidt -
r8220:6e6ebeb5 default
parent child Browse files
Show More
@@ -1,340 +1,340 b''
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, re, socket, sys, tempfile
9 import errno, getpass, os, re, socket, sys, tempfile
10 import config, traceback, util, error
10 import config, traceback, util, error
11
11
12 _booleans = {'1':True, 'yes':True, 'true':True, 'on':True,
12 _booleans = {'1':True, 'yes':True, 'true':True, 'on':True,
13 '0':False, 'no':False, 'false':False, 'off':False}
13 '0':False, 'no':False, 'false':False, 'off':False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self._traceback = False
18 self.quiet = self.verbose = self.debugflag = self._traceback = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = {}
23 self._trustusers = {}
24 self._trustgroups = {}
24 self._trustgroups = {}
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.fixconfig()
32 self.fixconfig()
33 else:
33 else:
34 # we always trust global config files
34 # we always trust global config files
35 for f in util.rcpath():
35 for f in util.rcpath():
36 self.readconfig(f, trust=True)
36 self.readconfig(f, trust=True)
37 def copy(self):
37 def copy(self):
38 return ui(self)
38 return self.__class__(self)
39
39
40 def _is_trusted(self, fp, f):
40 def _is_trusted(self, fp, f):
41 st = util.fstat(fp)
41 st = util.fstat(fp)
42 if util.isowner(fp, st):
42 if util.isowner(fp, st):
43 return True
43 return True
44
44
45 tusers, tgroups = self._trustusers, self._trustgroups
45 tusers, tgroups = self._trustusers, self._trustgroups
46 if '*' in tusers or '*' in tgroups:
46 if '*' in tusers or '*' in tgroups:
47 return True
47 return True
48
48
49 user = util.username(st.st_uid)
49 user = util.username(st.st_uid)
50 group = util.groupname(st.st_gid)
50 group = util.groupname(st.st_gid)
51 if user in tusers or group in tgroups or user == util.username():
51 if user in tusers or group in tgroups or user == util.username():
52 return True
52 return True
53
53
54 if self._reportuntrusted:
54 if self._reportuntrusted:
55 self.warn(_('Not trusting file %s from untrusted '
55 self.warn(_('Not trusting file %s from untrusted '
56 'user %s, group %s\n') % (f, user, group))
56 'user %s, group %s\n') % (f, user, group))
57 return False
57 return False
58
58
59 def readconfig(self, filename, root=None, trust=False,
59 def readconfig(self, filename, root=None, trust=False,
60 sections = None):
60 sections = None):
61 try:
61 try:
62 fp = open(filename)
62 fp = open(filename)
63 except IOError:
63 except IOError:
64 if not sections: # ignore unless we were looking for something
64 if not sections: # ignore unless we were looking for something
65 return
65 return
66 raise
66 raise
67
67
68 cfg = config.config()
68 cfg = config.config()
69 trusted = sections or trust or self._is_trusted(fp, filename)
69 trusted = sections or trust or self._is_trusted(fp, filename)
70
70
71 try:
71 try:
72 cfg.read(filename, fp, sections=sections)
72 cfg.read(filename, fp, sections=sections)
73 except error.ConfigError, inst:
73 except error.ConfigError, inst:
74 if trusted:
74 if trusted:
75 raise
75 raise
76 self.warn(_("Ignored: %s\n") % str(inst))
76 self.warn(_("Ignored: %s\n") % str(inst))
77
77
78 if trusted:
78 if trusted:
79 self._tcfg.update(cfg)
79 self._tcfg.update(cfg)
80 self._tcfg.update(self._ocfg)
80 self._tcfg.update(self._ocfg)
81 self._ucfg.update(cfg)
81 self._ucfg.update(cfg)
82 self._ucfg.update(self._ocfg)
82 self._ucfg.update(self._ocfg)
83
83
84 if root is None:
84 if root is None:
85 root = os.path.expanduser('~')
85 root = os.path.expanduser('~')
86 self.fixconfig(root=root)
86 self.fixconfig(root=root)
87
87
88 def fixconfig(self, root=None):
88 def fixconfig(self, root=None):
89 # translate paths relative to root (or home) into absolute paths
89 # translate paths relative to root (or home) into absolute paths
90 root = root or os.getcwd()
90 root = root or os.getcwd()
91 for c in self._tcfg, self._ucfg, self._ocfg:
91 for c in self._tcfg, self._ucfg, self._ocfg:
92 for n, p in c.items('paths'):
92 for n, p in c.items('paths'):
93 if p and "://" not in p and not os.path.isabs(p):
93 if p and "://" not in p and not os.path.isabs(p):
94 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
94 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
95
95
96 # update ui options
96 # update ui options
97 self.debugflag = self.configbool('ui', 'debug')
97 self.debugflag = self.configbool('ui', 'debug')
98 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
98 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
99 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
99 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
100 if self.verbose and self.quiet:
100 if self.verbose and self.quiet:
101 self.quiet = self.verbose = False
101 self.quiet = self.verbose = False
102 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
102 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
103 self._traceback = self.configbool('ui', 'traceback', False)
103 self._traceback = self.configbool('ui', 'traceback', False)
104
104
105 # update trust information
105 # update trust information
106 for user in self.configlist('trusted', 'users'):
106 for user in self.configlist('trusted', 'users'):
107 self._trustusers[user] = 1
107 self._trustusers[user] = 1
108 for group in self.configlist('trusted', 'groups'):
108 for group in self.configlist('trusted', 'groups'):
109 self._trustgroups[group] = 1
109 self._trustgroups[group] = 1
110
110
111 def setconfig(self, section, name, value):
111 def setconfig(self, section, name, value):
112 for cfg in (self._ocfg, self._tcfg, self._ucfg):
112 for cfg in (self._ocfg, self._tcfg, self._ucfg):
113 cfg.set(section, name, value)
113 cfg.set(section, name, value)
114 self.fixconfig()
114 self.fixconfig()
115
115
116 def _data(self, untrusted):
116 def _data(self, untrusted):
117 return untrusted and self._ucfg or self._tcfg
117 return untrusted and self._ucfg or self._tcfg
118
118
119 def configsource(self, section, name, untrusted=False):
119 def configsource(self, section, name, untrusted=False):
120 return self._data(untrusted).source(section, name) or 'none'
120 return self._data(untrusted).source(section, name) or 'none'
121
121
122 def config(self, section, name, default=None, untrusted=False):
122 def config(self, section, name, default=None, untrusted=False):
123 value = self._data(untrusted).get(section, name, default)
123 value = self._data(untrusted).get(section, name, default)
124 if self.debugflag and not untrusted and self._reportuntrusted:
124 if self.debugflag and not untrusted and self._reportuntrusted:
125 uvalue = self._ucfg.get(section, name)
125 uvalue = self._ucfg.get(section, name)
126 if uvalue is not None and uvalue != value:
126 if uvalue is not None and uvalue != value:
127 self.debug(_("ignoring untrusted configuration option "
127 self.debug(_("ignoring untrusted configuration option "
128 "%s.%s = %s\n") % (section, name, uvalue))
128 "%s.%s = %s\n") % (section, name, uvalue))
129 return value
129 return value
130
130
131 def configbool(self, section, name, default=False, untrusted=False):
131 def configbool(self, section, name, default=False, untrusted=False):
132 v = self.config(section, name, None, untrusted)
132 v = self.config(section, name, None, untrusted)
133 if v == None:
133 if v == None:
134 return default
134 return default
135 if v.lower() not in _booleans:
135 if v.lower() not in _booleans:
136 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
136 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
137 % (section, name, v))
137 % (section, name, v))
138 return _booleans[v.lower()]
138 return _booleans[v.lower()]
139
139
140 def configlist(self, section, name, default=None, untrusted=False):
140 def configlist(self, section, name, default=None, untrusted=False):
141 """Return a list of comma/space separated strings"""
141 """Return a list of comma/space separated strings"""
142 result = self.config(section, name, untrusted=untrusted)
142 result = self.config(section, name, untrusted=untrusted)
143 if result is None:
143 if result is None:
144 result = default or []
144 result = default or []
145 if isinstance(result, basestring):
145 if isinstance(result, basestring):
146 result = result.replace(",", " ").split()
146 result = result.replace(",", " ").split()
147 return result
147 return result
148
148
149 def has_section(self, section, untrusted=False):
149 def has_section(self, section, untrusted=False):
150 '''tell whether section exists in config.'''
150 '''tell whether section exists in config.'''
151 return section in self._data(untrusted)
151 return section in self._data(untrusted)
152
152
153 def configitems(self, section, untrusted=False):
153 def configitems(self, section, untrusted=False):
154 items = self._data(untrusted).items(section)
154 items = self._data(untrusted).items(section)
155 if self.debugflag and not untrusted and self._reportuntrusted:
155 if self.debugflag and not untrusted and self._reportuntrusted:
156 for k,v in self._ucfg.items(section):
156 for k,v in self._ucfg.items(section):
157 if self._tcfg.get(section, k) != v:
157 if self._tcfg.get(section, k) != v:
158 self.debug(_("ignoring untrusted configuration option "
158 self.debug(_("ignoring untrusted configuration option "
159 "%s.%s = %s\n") % (section, k, v))
159 "%s.%s = %s\n") % (section, k, v))
160 return items
160 return items
161
161
162 def walkconfig(self, untrusted=False):
162 def walkconfig(self, untrusted=False):
163 cfg = self._data(untrusted)
163 cfg = self._data(untrusted)
164 for section in cfg.sections():
164 for section in cfg.sections():
165 for name, value in self.configitems(section, untrusted):
165 for name, value in self.configitems(section, untrusted):
166 yield section, name, str(value).replace('\n', '\\n')
166 yield section, name, str(value).replace('\n', '\\n')
167
167
168 def username(self):
168 def username(self):
169 """Return default username to be used in commits.
169 """Return default username to be used in commits.
170
170
171 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
171 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
172 and stop searching if one of these is set.
172 and stop searching if one of these is set.
173 If not found and ui.askusername is True, ask the user, else use
173 If not found and ui.askusername is True, ask the user, else use
174 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
174 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
175 """
175 """
176 user = os.environ.get("HGUSER")
176 user = os.environ.get("HGUSER")
177 if user is None:
177 if user is None:
178 user = self.config("ui", "username")
178 user = self.config("ui", "username")
179 if user is None:
179 if user is None:
180 user = os.environ.get("EMAIL")
180 user = os.environ.get("EMAIL")
181 if user is None and self.configbool("ui", "askusername"):
181 if user is None and self.configbool("ui", "askusername"):
182 user = self.prompt(_("enter a commit username:"), default=None)
182 user = self.prompt(_("enter a commit username:"), default=None)
183 if user is None:
183 if user is None:
184 try:
184 try:
185 user = '%s@%s' % (util.getuser(), socket.getfqdn())
185 user = '%s@%s' % (util.getuser(), socket.getfqdn())
186 self.warn(_("No username found, using '%s' instead\n") % user)
186 self.warn(_("No username found, using '%s' instead\n") % user)
187 except KeyError:
187 except KeyError:
188 pass
188 pass
189 if not user:
189 if not user:
190 raise util.Abort(_("Please specify a username."))
190 raise util.Abort(_("Please specify a username."))
191 if "\n" in user:
191 if "\n" in user:
192 raise util.Abort(_("username %s contains a newline\n") % repr(user))
192 raise util.Abort(_("username %s contains a newline\n") % repr(user))
193 return user
193 return user
194
194
195 def shortuser(self, user):
195 def shortuser(self, user):
196 """Return a short representation of a user name or email address."""
196 """Return a short representation of a user name or email address."""
197 if not self.verbose: user = util.shortuser(user)
197 if not self.verbose: user = util.shortuser(user)
198 return user
198 return user
199
199
200 def _path(self, loc):
200 def _path(self, loc):
201 p = self.config('paths', loc)
201 p = self.config('paths', loc)
202 if p and '%%' in p:
202 if p and '%%' in p:
203 ui.warn('(deprecated \'\%\%\' in path %s=%s from %s)\n' %
203 ui.warn('(deprecated \'\%\%\' in path %s=%s from %s)\n' %
204 (loc, p, self.configsource('paths', loc)))
204 (loc, p, self.configsource('paths', loc)))
205 p = p.replace('%%', '%')
205 p = p.replace('%%', '%')
206 return p
206 return p
207
207
208 def expandpath(self, loc, default=None):
208 def expandpath(self, loc, default=None):
209 """Return repository location relative to cwd or from [paths]"""
209 """Return repository location relative to cwd or from [paths]"""
210 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
210 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
211 return loc
211 return loc
212
212
213 path = self._path(loc)
213 path = self._path(loc)
214 if not path and default is not None:
214 if not path and default is not None:
215 path = self._path(default)
215 path = self._path(default)
216 return path or loc
216 return path or loc
217
217
218 def pushbuffer(self):
218 def pushbuffer(self):
219 self._buffers.append([])
219 self._buffers.append([])
220
220
221 def popbuffer(self):
221 def popbuffer(self):
222 return "".join(self._buffers.pop())
222 return "".join(self._buffers.pop())
223
223
224 def write(self, *args):
224 def write(self, *args):
225 if self._buffers:
225 if self._buffers:
226 self._buffers[-1].extend([str(a) for a in args])
226 self._buffers[-1].extend([str(a) for a in args])
227 else:
227 else:
228 for a in args:
228 for a in args:
229 sys.stdout.write(str(a))
229 sys.stdout.write(str(a))
230
230
231 def write_err(self, *args):
231 def write_err(self, *args):
232 try:
232 try:
233 if not sys.stdout.closed: sys.stdout.flush()
233 if not sys.stdout.closed: sys.stdout.flush()
234 for a in args:
234 for a in args:
235 sys.stderr.write(str(a))
235 sys.stderr.write(str(a))
236 # stderr may be buffered under win32 when redirected to files,
236 # stderr may be buffered under win32 when redirected to files,
237 # including stdout.
237 # including stdout.
238 if not sys.stderr.closed: sys.stderr.flush()
238 if not sys.stderr.closed: sys.stderr.flush()
239 except IOError, inst:
239 except IOError, inst:
240 if inst.errno != errno.EPIPE:
240 if inst.errno != errno.EPIPE:
241 raise
241 raise
242
242
243 def flush(self):
243 def flush(self):
244 try: sys.stdout.flush()
244 try: sys.stdout.flush()
245 except: pass
245 except: pass
246 try: sys.stderr.flush()
246 try: sys.stderr.flush()
247 except: pass
247 except: pass
248
248
249 def interactive(self):
249 def interactive(self):
250 return self.configbool("ui", "interactive") or sys.stdin.isatty()
250 return self.configbool("ui", "interactive") or sys.stdin.isatty()
251
251
252 def _readline(self, prompt=''):
252 def _readline(self, prompt=''):
253 if sys.stdin.isatty():
253 if sys.stdin.isatty():
254 try:
254 try:
255 # magically add command line editing support, where
255 # magically add command line editing support, where
256 # available
256 # available
257 import readline
257 import readline
258 # force demandimport to really load the module
258 # force demandimport to really load the module
259 readline.read_history_file
259 readline.read_history_file
260 # windows sometimes raises something other than ImportError
260 # windows sometimes raises something other than ImportError
261 except Exception:
261 except Exception:
262 pass
262 pass
263 line = raw_input(prompt)
263 line = raw_input(prompt)
264 # When stdin is in binary mode on Windows, it can cause
264 # When stdin is in binary mode on Windows, it can cause
265 # raw_input() to emit an extra trailing carriage return
265 # raw_input() to emit an extra trailing carriage return
266 if os.linesep == '\r\n' and line and line[-1] == '\r':
266 if os.linesep == '\r\n' and line and line[-1] == '\r':
267 line = line[:-1]
267 line = line[:-1]
268 return line
268 return line
269
269
270 def prompt(self, msg, pat=None, default="y"):
270 def prompt(self, msg, pat=None, default="y"):
271 """Prompt user with msg, read response, and ensure it matches pat
271 """Prompt user with msg, read response, and ensure it matches pat
272
272
273 If not interactive -- the default is returned
273 If not interactive -- the default is returned
274 """
274 """
275 if not self.interactive():
275 if not self.interactive():
276 self.note(msg, ' ', default, "\n")
276 self.note(msg, ' ', default, "\n")
277 return default
277 return default
278 while True:
278 while True:
279 try:
279 try:
280 r = self._readline(msg + ' ')
280 r = self._readline(msg + ' ')
281 if not r:
281 if not r:
282 return default
282 return default
283 if not pat or re.match(pat, r):
283 if not pat or re.match(pat, r):
284 return r
284 return r
285 else:
285 else:
286 self.write(_("unrecognized response\n"))
286 self.write(_("unrecognized response\n"))
287 except EOFError:
287 except EOFError:
288 raise util.Abort(_('response expected'))
288 raise util.Abort(_('response expected'))
289
289
290 def getpass(self, prompt=None, default=None):
290 def getpass(self, prompt=None, default=None):
291 if not self.interactive(): return default
291 if not self.interactive(): return default
292 try:
292 try:
293 return getpass.getpass(prompt or _('password: '))
293 return getpass.getpass(prompt or _('password: '))
294 except EOFError:
294 except EOFError:
295 raise util.Abort(_('response expected'))
295 raise util.Abort(_('response expected'))
296 def status(self, *msg):
296 def status(self, *msg):
297 if not self.quiet: self.write(*msg)
297 if not self.quiet: self.write(*msg)
298 def warn(self, *msg):
298 def warn(self, *msg):
299 self.write_err(*msg)
299 self.write_err(*msg)
300 def note(self, *msg):
300 def note(self, *msg):
301 if self.verbose: self.write(*msg)
301 if self.verbose: self.write(*msg)
302 def debug(self, *msg):
302 def debug(self, *msg):
303 if self.debugflag: self.write(*msg)
303 if self.debugflag: self.write(*msg)
304 def edit(self, text, user):
304 def edit(self, text, user):
305 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
305 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
306 text=True)
306 text=True)
307 try:
307 try:
308 f = os.fdopen(fd, "w")
308 f = os.fdopen(fd, "w")
309 f.write(text)
309 f.write(text)
310 f.close()
310 f.close()
311
311
312 editor = self.geteditor()
312 editor = self.geteditor()
313
313
314 util.system("%s \"%s\"" % (editor, name),
314 util.system("%s \"%s\"" % (editor, name),
315 environ={'HGUSER': user},
315 environ={'HGUSER': user},
316 onerr=util.Abort, errprefix=_("edit failed"))
316 onerr=util.Abort, errprefix=_("edit failed"))
317
317
318 f = open(name)
318 f = open(name)
319 t = f.read()
319 t = f.read()
320 f.close()
320 f.close()
321 t = re.sub("(?m)^HG:.*\n", "", t)
321 t = re.sub("(?m)^HG:.*\n", "", t)
322 finally:
322 finally:
323 os.unlink(name)
323 os.unlink(name)
324
324
325 return t
325 return t
326
326
327 def traceback(self):
327 def traceback(self):
328 '''print exception traceback if traceback printing enabled.
328 '''print exception traceback if traceback printing enabled.
329 only to call in exception handler. returns true if traceback
329 only to call in exception handler. returns true if traceback
330 printed.'''
330 printed.'''
331 if self._traceback:
331 if self._traceback:
332 traceback.print_exc()
332 traceback.print_exc()
333 return self._traceback
333 return self._traceback
334
334
335 def geteditor(self):
335 def geteditor(self):
336 '''return editor to use'''
336 '''return editor to use'''
337 return (os.environ.get("HGEDITOR") or
337 return (os.environ.get("HGEDITOR") or
338 self.config("ui", "editor") or
338 self.config("ui", "editor") or
339 os.environ.get("VISUAL") or
339 os.environ.get("VISUAL") or
340 os.environ.get("EDITOR", "vi"))
340 os.environ.get("EDITOR", "vi"))
General Comments 0
You need to be logged in to leave comments. Login now