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