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