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